diff --git a/src/Compilation.zig b/src/Compilation.zig index 80ca20d7d37475606c0b957a07f2b30b42bda533..6561f88312664801ea4dc1d217ecf85ae413c1af 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3675,11 +3675,11 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs, @ptrCast(wasm.object_relocations_table.values())); addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.kind))); addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.index))); - addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.tag))); - addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.offset))); + addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.tag))); + addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.offset))); // TODO handle the union safety field - //addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.pointee))); - addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.addend))); + //addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.pointee))); + addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.addend))); addBuf(&bufs, @ptrCast(wasm.uav_fixups.items)); addBuf(&bufs, @ptrCast(wasm.nav_fixups.items)); addBuf(&bufs, @ptrCast(wasm.func_table_fixups.items)); diff --git a/src/codegen.zig b/src/codegen.zig index 4ad10d48c05c59608f06956209f7f9b66a6d2775..326a9f1e2f52b90bb40bf4006000c3864a47fed5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -767,11 +767,9 @@ fn lowerNavRef( offset: u64, ) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; - const gpa = zcu.gpa; const ip = &zcu.intern_pool; const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); - const is_obj = lf.comp.config.output_mode == .Obj; const nav_ty = Type.fromInterned(ip.getNav(nav_index).resolved.?.type); if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) and ip.getNav(nav_index).getExtern(ip) == null) { @@ -786,34 +784,7 @@ fn lowerNavRef( dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); - if (nav_ty.zigTypeTag(zcu) == .@"fn") { - const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); - if (!gop.found_existing) gop.value_ptr.* = {}; - if (is_obj) { - @panic("TODO add out_reloc for this"); - } else { - try wasm.func_table_fixups.append(gpa, .{ - .table_index = @fromBackingInt(@intCast(gop.index)), - .offset = @intCast(w.end), - }); - } - } else { - if (is_obj) { - try wasm.out_relocs.append(gpa, .{ - .offset = @intCast(w.end), - .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) }, - .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, - .addend = @intCast(offset), - }); - } else { - try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1); - wasm.nav_fixups.appendAssumeCapacity(.{ - .navs_exe_index = try wasm.refNavExe(nav_index), - .offset = @intCast(w.end), - .addend = @intCast(offset), - }); - } - } + try wasm.addNavReloc(w.end, nav_index, nav_ty, @intCast(offset)); try w.splatByteAll(0, ptr_width_bytes); return; }, diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index 2848a5f2573c75c73036aa2ff19f583403a9085f..8d6e18385f8de67b92f5ba72cde959fc6df83b91 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -4923,7 +4923,8 @@ fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerErro const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; const offset: u64 = prev_offset + ptr.byte_offset; return switch (ptr.base_addr) { - .nav => |nav| return if (Type.fromInterned(ip.getNav(nav).resolved.?.type).isRuntimeFnOrHasRuntimeBits(zcu)) + .nav => |nav| return if (ip.getNav(nav).getExtern(ip) != null or + Type.fromInterned(ip.getNav(nav).resolved.?.type).isRuntimeFnOrHasRuntimeBits(zcu)) .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } } else .{ .imm32 = @intCast(zcu.navAlignment(nav).forward(@as(u32, 0xaaaaaaaa))) }, @@ -5607,6 +5608,8 @@ fn bitcastClass(cg: *CodeGen, ty: Type) BitcastClass { } fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!?WValue { + if (dest_ty.eql(src_ty)) return null; + const zcu = cg.pt.zcu; const src_class = cg.bitcastClass(src_ty); const dest_class = cg.bitcastClass(dest_ty); diff --git a/src/codegen/wasm/Emit.zig b/src/codegen/wasm/Emit.zig index ccc75b9b536871e5010f1a5155cdd0ae4abf9301..a83597dfc4fb3b0749a66fdf0c7e62c8995457c5 100644 --- a/src/codegen/wasm/Emit.zig +++ b/src/codegen/wasm/Emit.zig @@ -21,7 +21,7 @@ pub const Error = error{ OutOfMemory, }; -pub fn lowerToCode(emit: *Emit) Error!void { +pub fn lower(emit: *Emit) Error!void { const mir = &emit.mir; const code = emit.code; const wasm = emit.wasm; @@ -31,6 +31,47 @@ pub fn lowerToCode(emit: *Emit) Error!void { const target = &comp.root_mod.resolved_target.result; const is_wasm32 = target.cpu.arch == .wasm32; + // Write the locals in the prologue of the function body. + try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38); + + writeUleb128(code, @as(u32, @intCast(mir.locals.len))); + + for (mir.locals) |local| { + writeUleb128(code, @as(u32, 1)); + code.appendAssumeCapacity(@backingInt(local)); + } + + // Stack management section of function prologue. + const stack_alignment = mir.prologue.flags.stack_alignment; + if (stack_alignment.toByteUnits()) |align_bytes| { + // load stack pointer + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_get)); + try appendStackPointerGlobalIndex(wasm, code, is_obj); + // store stack pointer so we can restore it when we return from the function + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.local_tee)); + writeUleb128(code, mir.prologue.sp_local); + // get the total stack size + const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size)); + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const)); + writeSleb128(code, aligned_stack); + // subtract it from the current stack pointer + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_sub)); + // Get negative stack alignment + const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const)); + writeSleb128(code, neg_stack_align); + // Bitwise-and the value to get the new stack pointer to ensure the + // pointers are aligned with the abi alignment. + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_and)); + // The bottom will be used to calculate all stack pointer offsets. + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.local_tee)); + writeUleb128(code, mir.prologue.bottom_stack_local); + // Store the current stack pointer value into the global stack pointer so other function calls will + // start from this value instead and not overwrite the current stack. + code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_set)); + try appendStackPointerGlobalIndex(wasm, code, is_obj); + } + const tags = mir.instructions.items(.tag); const datas = mir.instructions.items(.data); var inst: u32 = 0; @@ -78,14 +119,21 @@ pub fn lowerToCode(emit: *Emit) Error!void { continue :loop tags[inst]; }, .func_ref => { - const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @fromBackingInt(@intCast( - wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?, - )); - code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const)); + try code.ensureUnusedCapacity(gpa, 11); + const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; + code.appendAssumeCapacity(@backingInt(opcode)); if (is_obj) { - @panic("TODO"); + try wasm.zcu_relocations.append(gpa, .{ + .offset = @intCast(code.items.len), + .pointee = .{ .function_nav = datas[inst].nav_index }, + .tag = if (is_wasm32) .table_index_sleb else .table_index_sleb64, + .addend = 0, + }); + appendSlebRelocPlaceholder(code, is_wasm32); } else { - writeSleb128(code, 1 + @backingInt(indirect_func_idx)); + const function_index = Wasm.OutputFunctionIndex.fromIpNav(wasm, datas[inst].nav_index); + const table_index = wasm.flush_buffer.indirect_function_table.getIndex(function_index).? + 1; + writeSleb128(code, table_index); } inst += 1; continue :loop tags[inst]; @@ -105,18 +153,17 @@ pub fn lowerToCode(emit: *Emit) Error!void { continue :loop tags[inst]; }, .error_name_table_ref => { - wasm.error_name_table_ref_count += 1; try code.ensureUnusedCapacity(gpa, 11); const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; code.appendAssumeCapacity(@backingInt(opcode)); if (is_obj) { - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() }, - .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, + .pointee = .{ .data_resolution = .__zig_error_name_table }, + .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64, .addend = 0, }); - code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); + appendSlebRelocPlaceholder(code, is_wasm32); inst += 1; continue :loop tags[inst]; @@ -164,13 +211,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { try code.ensureUnusedCapacity(gpa, 6); code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call)); if (is_obj) { - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) }, + .pointee = .{ .function_nav = datas[inst].nav_index }, .tag = .function_index_leb, .addend = 0, }); - code.appendNTimesAssumeCapacity(0, 5); + appendUlebRelocPlaceholder(code); } else { appendOutputFunctionIndex(code, .fromIpNav(wasm, datas[inst].nav_index)); } @@ -191,13 +238,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { ).?; if (is_obj) { code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call_indirect)); - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), .pointee = .{ .type_index = func_ty_index }, .tag = .type_index_leb, .addend = 0, }); - code.appendNTimesAssumeCapacity(0, 5); + appendUlebRelocPlaceholder(code); } else { const index: Wasm.Flush.FuncTypeIndex = @fromBackingInt(@intCast(wasm.flush_buffer.func_types.getIndex(func_ty_index) orelse { // In this case we tried to call a function pointer for @@ -224,13 +271,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { try code.ensureUnusedCapacity(gpa, 6); code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call)); if (is_obj) { - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.tagTableIndexSymbolIndex(datas[inst].ip_index) }, + .pointee = .{ .tag_function = datas[inst].ip_index }, .tag = .function_index_leb, .addend = 0, }); - code.appendNTimesAssumeCapacity(0, 5); + appendUlebRelocPlaceholder(code); } else { appendOutputFunctionIndex(code, .fromTagIndexType(wasm, datas[inst].ip_index)); } @@ -244,14 +291,20 @@ pub fn lowerToCode(emit: *Emit) Error!void { const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; code.appendAssumeCapacity(@backingInt(opcode)); if (is_obj) { - @panic("TODO"); + try wasm.zcu_relocations.append(gpa, .{ + .offset = @intCast(code.items.len), + .pointee = .{ .data_resolution = .__zig_tag_name_table }, + .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64, + .addend = @intCast(wasm.tagIndexTableOffset(datas[inst].ip_index)), + }); + appendSlebRelocPlaceholder(code, is_wasm32); } else { const addr: u32 = wasm.tagIndexTableAddr(datas[inst].ip_index); writeSleb128(code, addr); - - inst += 1; - continue :loop tags[inst]; } + + inst += 1; + continue :loop tags[inst]; }, .call_intrinsic => { @@ -263,13 +316,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { try code.ensureUnusedCapacity(gpa, 6); code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call)); if (is_obj) { - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) }, + .pointee = .{ .function_name = symbol_name }, .tag = .function_index_leb, .addend = 0, }); - code.appendNTimesAssumeCapacity(0, 5); + appendUlebRelocPlaceholder(code); } else { appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name)); } @@ -281,18 +334,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { .global_set_sp => { try code.ensureUnusedCapacity(gpa, 6); code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_set)); - if (is_obj) { - try wasm.out_relocs.append(gpa, .{ - .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() }, - .tag = .global_index_leb, - .addend = 0, - }); - code.appendNTimesAssumeCapacity(0, 5); - } else { - const sp_global: Wasm.GlobalIndex = .stack_pointer; - writeUleb128(code, @backingInt(sp_global)); - } + try appendStackPointerGlobalIndex(wasm, code, is_obj); inst += 1; continue :loop tags[inst]; @@ -960,13 +1002,13 @@ fn uavRefObj(wasm: *Wasm, code: *ArrayList(u8), value: InternPool.Index, offset: try code.ensureUnusedCapacity(gpa, 11); code.appendAssumeCapacity(@backingInt(opcode)); - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) }, - .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, + .pointee = .{ .data_uav = value }, + .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64, .addend = offset, }); - code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); + appendSlebRelocPlaceholder(code, is_wasm32); } fn uavRefExe(wasm: *Wasm, code: *ArrayList(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void { @@ -995,13 +1037,13 @@ fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32: const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; code.appendAssumeCapacity(@backingInt(opcode)); if (is_obj) { - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(code.items.len), - .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) }, - .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, + .pointee = .{ .data_nav = data.nav_index }, + .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64, .addend = data.offset, }); - code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); + appendSlebRelocPlaceholder(code, is_wasm32); } else { const addr = wasm.navAddr(data.nav_index); writeSleb128(code, @as(u32, @intCast(@as(i64, addr) + data.offset))); @@ -1012,6 +1054,40 @@ fn appendOutputFunctionIndex(code: *ArrayList(u8), i: Wasm.OutputFunctionIndex) writeUleb128(code, @backingInt(i)); } +fn appendStackPointerGlobalIndex( + wasm: *Wasm, + code: *ArrayList(u8), + is_obj: bool, +) Error!void { + if (is_obj) { + try wasm.zcu_relocations.append(wasm.base.comp.gpa, .{ + .offset = @intCast(code.items.len), + .pointee = .stack_pointer, + .tag = .global_index_leb, + .addend = 0, + }); + appendUlebRelocPlaceholder(code); + } else { + const sp_global: Wasm.GlobalIndex = .stack_pointer; + writeUleb128(code, @backingInt(sp_global)); + } +} + +fn appendUlebRelocPlaceholder(code: *ArrayList(u8)) void { + code.appendSliceAssumeCapacity(&.{ 0x80, 0x80, 0x80, 0x80, 0x00 }); +} + +fn appendSlebRelocPlaceholder(code: *ArrayList(u8), is_wasm32: bool) void { + if (is_wasm32) { + code.appendSliceAssumeCapacity(&.{ 0x80, 0x80, 0x80, 0x80, 0x00 }); + } else { + code.appendSliceAssumeCapacity(&.{ + 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x00, + }); + } +} + fn writeUleb128(code: *ArrayList(u8), arg: anytype) void { var w: std.Io.Writer = .fixed(code.unusedCapacitySlice()); w.writeUleb128(arg) catch unreachable; diff --git a/src/codegen/wasm/Mir.zig b/src/codegen/wasm/Mir.zig index bbb41312990a836c336b6a3aa303ba2f0e363521..5f602496e901acc56864a8e45143fd7e14d193f7 100644 --- a/src/codegen/wasm/Mir.zig +++ b/src/codegen/wasm/Mir.zig @@ -114,7 +114,7 @@ pub const Inst = struct { /// /// Uses `payload` pointing to a `NavRefOff`. nav_ref_off, - /// Lowers to an i32_const which is the index of the function in the + /// Lowers to an iNN_const which is the index of the function in the /// table section. /// /// Uses `nav_index`. @@ -679,60 +679,12 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { } pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayList(u8)) std.mem.Allocator.Error!void { - const gpa = wasm.base.comp.gpa; - - // Write the locals in the prologue of the function body. - try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38); - - var w: std.Io.Writer = .fixed(code.unusedCapacitySlice()); - - w.writeLeb128(@as(u32, @intCast(mir.locals.len))) catch unreachable; - - for (mir.locals) |local| { - w.writeLeb128(@as(u32, 1)) catch unreachable; - w.writeByte(@backingInt(local)) catch unreachable; - } - - // Stack management section of function prologue. - const stack_alignment = mir.prologue.flags.stack_alignment; - if (stack_alignment.toByteUnits()) |align_bytes| { - const sp_global: Wasm.GlobalIndex = .stack_pointer; - // load stack pointer - w.writeByte(@backingInt(std.wasm.Opcode.global_get)) catch unreachable; - w.writeUleb128(@backingInt(sp_global)) catch unreachable; - // store stack pointer so we can restore it when we return from the function - w.writeByte(@backingInt(std.wasm.Opcode.local_tee)) catch unreachable; - w.writeUleb128(mir.prologue.sp_local) catch unreachable; - // get the total stack size - const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size)); - w.writeByte(@backingInt(std.wasm.Opcode.i32_const)) catch unreachable; - w.writeSleb128(aligned_stack) catch unreachable; - // subtract it from the current stack pointer - w.writeByte(@backingInt(std.wasm.Opcode.i32_sub)) catch unreachable; - // Get negative stack alignment - const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; - w.writeByte(@backingInt(std.wasm.Opcode.i32_const)) catch unreachable; - w.writeSleb128(neg_stack_align) catch unreachable; - // Bitwise-and the value to get the new stack pointer to ensure the - // pointers are aligned with the abi alignment. - w.writeByte(@backingInt(std.wasm.Opcode.i32_and)) catch unreachable; - // The bottom will be used to calculate all stack pointer offsets. - w.writeByte(@backingInt(std.wasm.Opcode.local_tee)) catch unreachable; - w.writeUleb128(mir.prologue.bottom_stack_local) catch unreachable; - // Store the current stack pointer value into the global stack pointer so other function calls will - // start from this value instead and not overwrite the current stack. - w.writeByte(@backingInt(std.wasm.Opcode.global_set)) catch unreachable; - w.writeUleb128(@backingInt(sp_global)) catch unreachable; - } - - code.items.len += w.end; - var emit: Emit = .{ .mir = mir.*, .wasm = wasm, .code = code, }; - try emit.lowerToCode(); + try emit.lower(); } pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index a1445d3b4fbe49d94f43cb88cab30a237f4cd7cc..6ec027ad7f64991b38266e16ad04ee35d66ab14b 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -133,25 +133,21 @@ object_total_sections: u32 = 0, /// All comdat symbols from all objects concatenated. object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty, -/// Relocations to be emitted into an object file. Remains empty when not -/// emitting an object file. -out_relocs: std.MultiArrayList(OutReloc) = .empty, +/// Relocations produced by Zig code and data lowering. These retain semantic +/// targets until `flush`, where final output indexes are known. +zcu_relocations: std.MultiArrayList(ZcuRelocation) = .empty, /// List of locations within `string_bytes` that must be patched with the virtual /// memory address of a Uav during `flush`. -/// When emitting an object file, `out_relocs` is used instead. +/// When emitting an object file, `zcu_relocations` is used instead. uav_fixups: std.ArrayList(UavFixup) = .empty, /// List of locations within `string_bytes` that must be patched with the virtual /// memory address of a Nav during `flush`. -/// When emitting an object file, `out_relocs` is used instead. +/// When emitting an object file, `zcu_relocations` is used instead. /// No functions here only global variables. nav_fixups: std.ArrayList(NavFixup) = .empty, /// When a nav reference is a function pointer, this tracks the required function /// table entry index that needs to overwrite the code in the final output. func_table_fixups: std.ArrayList(FuncTableFixup) = .empty, -/// Symbols to be emitted into an object file. Remains empty when not emitting -/// an object file. -symbol_table: std.array_hash_map.Auto(String, void) = .empty, - /// When importing objects from the host environment, a name must be supplied. /// LLVM uses "env" by default when none is given. /// This value is passed to object files since wasm tooling conventions provides @@ -244,7 +240,9 @@ function_imports: std.array_hash_map.Auto(String, FunctionImportId) = .empty, /// remove elements from the table, and the remainder are either undefined /// symbol errors, or symbol table entries depending on the output mode. data_imports: std.array_hash_map.Auto(String, DataImportId) = .empty, -/// Set of data symbols that will appear in the final binary. Used to populate +/// Set of data symbols that will appear in the final binary when outputting an object file. +datas: std.array_hash_map.Auto(ObjectDataImport.Resolution, void) = .empty, +/// Set of data segment symbols that will appear in the final binary. Used to populate /// `Flush.data_segments` before sorting. data_segments: std.array_hash_map.Auto(DataSegmentId, void) = .empty, @@ -302,11 +300,6 @@ pub const TagNameOff = extern struct { len: u32, }; -/// Index into `Wasm.zcu_indirect_function_set`. -pub const ZcuIndirectFunctionSetIndex = enum(u32) { - _, -}; - pub const UavFixup = extern struct { uavs_exe_index: UavsExeIndex, /// Index into `string_bytes`. @@ -315,14 +308,14 @@ pub const UavFixup = extern struct { }; pub const NavFixup = extern struct { - navs_exe_index: NavsExeIndex, + nav_index: InternPool.Nav.Index, /// Index into `string_bytes`. offset: u32, addend: u32, }; pub const FuncTableFixup = extern struct { - table_index: ZcuIndirectFunctionSetIndex, + nav_index: InternPool.Nav.Index, /// Index into `string_bytes`. offset: u32, }; @@ -355,7 +348,9 @@ pub const FunctionIndex = enum(u32) { pub fn fromSymbolName(wasm: *const Wasm, name: String) ?FunctionIndex { if (wasm.object_function_imports.getPtr(name)) |import| { - return fromResolution(wasm, import.resolution); + if (import.resolution != .unresolved) { + return fromResolution(wasm, import.resolution); + } } if (wasm.function_exports.get(name)) |index| return index; if (wasm.hidden_function_exports.get(name)) |index| return index; @@ -374,7 +369,8 @@ pub const GlobalExport = extern struct { }; /// 0. Index into `Flush.function_imports` -/// 1. Index into `functions`. +/// 1. Index into `Flush.intrinsic_function_imports` +/// 2. Index into `functions`. /// /// Note that function_imports indexes are subject to swap removals during /// `flush`. @@ -386,7 +382,11 @@ pub const OutputFunctionIndex = enum(u32) { } pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex { - return @fromBackingInt(@intCast(wasm.flush_buffer.function_imports.entries.len + @backingInt(index))); + return @fromBackingInt(@intCast( + wasm.flush_buffer.function_imports.entries.len + + wasm.flush_buffer.intrinsic_function_imports.entries.len + + @backingInt(index), + )); } pub fn fromObjectFunction(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex { @@ -429,6 +429,9 @@ pub const OutputFunctionIndex = enum(u32) { pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputFunctionIndex { if (wasm.flush_buffer.function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); + if (wasm.flush_buffer.intrinsic_function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast( + wasm.flush_buffer.function_imports.entries.len + i, + )); return fromFunctionIndex(wasm, FunctionIndex.fromSymbolName(wasm, name) orelse { if (std.debug.runtime_safety) { std.debug.panic("function index for symbol not found: {s}", .{name.slice(wasm)}); @@ -437,6 +440,56 @@ pub const OutputFunctionIndex = enum(u32) { } }; +// Order +// 0. Flush.data_imports +// 1. Wasm.datas +pub const OutputDataIndex = enum(u32) { + _, + + pub fn fromSymbolName(wasm: *const Wasm, name: String) OutputDataIndex { + if (wasm.flush_buffer.data_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); + if (wasm.object_data_imports.getPtr(name)) |import| { + if (import.resolution != .unresolved) return fromResolution(wasm, import.resolution).?; + } + if (wasm.flush_buffer.data_exports.get(name)) |symbol| return fromResolution(wasm, symbol.resolution).?; + if (std.debug.runtime_safety) { + std.debug.panic("data index for symbol not found: {s}", .{name.slice(wasm)}); + } else unreachable; + } + + pub fn fromObjectData(wasm: *const Wasm, index: ObjectData.Index) OutputDataIndex { + return fromResolution(wasm, .fromObjectDataIndex(wasm, index)).?; + } + + pub fn fromResolution(wasm: *const Wasm, resolution: ObjectDataImport.Resolution) ?OutputDataIndex { + const i = wasm.datas.getIndex(resolution) orelse return null; + return @fromBackingInt(@intCast(wasm.flush_buffer.data_imports.entries.len + i)); + } + + pub fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) OutputDataIndex { + const comp = wasm.base.comp; + const resolution: ObjectDataImport.Resolution = if (comp.config.output_mode == .Obj) + .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)) }) + else + .pack(wasm, .{ .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)) }); + return fromResolution(wasm, resolution).?; + } + + pub fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) OutputDataIndex { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + if (nav.getExtern(ip)) |ext| { + return fromSymbolName(wasm, wasm.getExistingString(ext.name.toSlice(ip)).?); + } + const resolution: ObjectDataImport.Resolution = if (wasm.base.comp.config.output_mode == .Obj) + .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)) }) + else + .pack(wasm, .{ .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)) }); + return fromResolution(wasm, resolution).?; + } +}; + /// Index into `Wasm.globals`. pub const GlobalIndex = enum(u32) { _, @@ -452,17 +505,17 @@ pub const GlobalIndex = enum(u32) { return .stack_pointer; } - pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution { - return &f.globals.items[@backingInt(index)]; + pub fn fromResolution(wasm: *const Wasm, resolution: GlobalImport.Resolution) ?GlobalIndex { + const i = wasm.globals.getIndex(resolution) orelse return null; + return @fromBackingInt(@intCast(wasm.flush_buffer.global_imports.entries.len + i)); } pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) ?GlobalIndex { - const i = wasm.globals.getIndex(.fromIpNav(wasm, nav_index)) orelse return null; - return @fromBackingInt(@intCast(i)); + return fromResolution(wasm, .fromIpNav(wasm, nav_index)); } pub fn fromObjectGlobal(wasm: *const Wasm, i: ObjectGlobalIndex) GlobalIndex { - return @fromBackingInt(@intCast(wasm.globals.getIndex(.fromObjectGlobal(wasm, i)).?)); + return fromResolution(wasm, .fromObjectGlobal(wasm, i)).?; } pub fn fromObjectGlobalHandlingWeak(wasm: *const Wasm, index: ObjectGlobalIndex) GlobalIndex { @@ -474,8 +527,9 @@ pub const GlobalIndex = enum(u32) { } pub fn fromSymbolName(wasm: *const Wasm, name: String) GlobalIndex { + if (wasm.flush_buffer.global_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); const import = wasm.object_global_imports.getPtr(name).?; - return @fromBackingInt(@intCast(wasm.globals.getIndex(import.resolution).?)); + return fromResolution(wasm, import.resolution).?; } }; @@ -483,10 +537,6 @@ pub const GlobalIndex = enum(u32) { pub const TableIndex = enum(u32) { _, - pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution { - return &f.tables.items[@backingInt(index)]; - } - pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex { return @fromBackingInt(@intCast(wasm.tables.getIndex(.fromObjectTable(i)).?)); } @@ -668,9 +718,10 @@ pub const SymbolFlags = packed struct(u32) { flags.ref_type = .funcref; } - pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool) bool { + pub fn isIncluded(flags: SymbolFlags, is_dynamic: bool, is_obj: bool) bool { return flags.exported or (is_dynamic and !flags.visibility_hidden) or + (is_obj and flags.binding != .local) or (flags.no_strip and flags.must_link); } @@ -696,8 +747,8 @@ pub const SymbolFlags = packed struct(u32) { /// Masks off the Zig-specific stuff. pub fn toAbiInteger(flags: SymbolFlags) u32 { var copy = flags; - copy.initZigSpecific(false, false); - return @bitCast(copy); + copy.initZigSpecific(false, flags.no_strip); + return @backingInt(copy); } }; @@ -812,7 +863,7 @@ pub const UavsExeIndex = enum(u32) { /// Used when emitting a relocatable object. pub const ZcuDataObj = extern struct { code: DataPayload, - relocs: OutReloc.Slice, + relocs: ZcuRelocation.Slice, }; /// Used when not emitting a relocatable object. @@ -855,7 +906,9 @@ const ZcuDataStarts = struct { var uavs_i = zds.uavs_i; while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) { // Call to `lowerZcuData` here possibly creates more entries in these tables. - wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]); + const uav = wasm.uavs_obj.keys()[uavs_i]; + const zcu_data = try lowerZcuData(wasm, pt, uav); + wasm.uavs_obj.values()[uavs_i] = zcu_data; } } @@ -906,6 +959,51 @@ pub const ZcuFunc = union { return &wasm.zcu_funcs.values()[@backingInt(i)]; } + pub fn flags(i: @This(), wasm: *const Wasm) SymbolFlags { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const ip_index = i.key(wasm).*; + switch (ip.indexToKey(ip_index)) { + .func => |func| { + const nav = ip.getNav(func.owner_nav); + if (nav.getExtern(ip)) |ext| { + const name_slice = ext.name.toSlice(ip); + const name_string = wasm.getExistingString(name_slice).?; + return .{ + .binding = switch (ext.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = switch (ext.visibility) { + .default => false, + .hidden => true, + .protected => false, + }, + .undefined = false, + .exported = wasm.missing_exports.contains(name_string), + .explicit_name = false, + .no_strip = false, + .tls = ext.is_threadlocal, + .absolute = false, + }; + } else { + return .{ + .binding = .local, + .tls = nav.resolved.?.@"threadlocal", + }; + } + }, + .enum_type => { + return .{ + .binding = .local, + }; + }, + else => unreachable, + } + } + pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 { const zcu = wasm.base.comp.zcu.?; const ip = &zcu.intern_pool; @@ -1034,6 +1132,15 @@ pub const FunctionImport = extern struct { return pack(wasm, .{ .object_function = object_function }); } + pub fn flags(r: Resolution, wasm: *Wasm) SymbolFlags { + return switch (unpack(r, wasm)) { + .unresolved => unreachable, + .__wasm_apply_global_tls_relocs, .__wasm_call_ctors, .__wasm_init_memory, .__wasm_init_tls => unreachable, + .object_function => |i| i.ptr(wasm).flags, + .zcu_func => |i| i.flags(wasm), + }; + } + pub fn isNavOrUnresolved(r: Resolution, wasm: *const Wasm) bool { return switch (r.unpack(wasm)) { .unresolved, .zcu_func => true, @@ -1136,6 +1243,7 @@ pub const GlobalImport = extern struct { __tls_base, __tls_size, // Next, index into `object_globals`. + // Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object. // Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object. _, @@ -1150,6 +1258,8 @@ pub const GlobalImport = extern struct { __tls_base, __tls_size, object_global: ObjectGlobalIndex, + uav_exe: UavsExeIndex, + uav_obj: UavsObjIndex, nav_exe: NavsExeIndex, nav_obj: NavsObjIndex, }; @@ -1170,12 +1280,22 @@ pub const GlobalImport = extern struct { return .{ .object_global = @fromBackingInt(@intCast(object_global_index)) }; const comp = wasm.base.comp; const is_obj = comp.config.output_mode == .Obj; - const nav_index = object_global_index - wasm.object_globals.items.len; - return if (is_obj) .{ - .nav_obj = @fromBackingInt(@intCast(nav_index)), - } else .{ - .nav_exe = @fromBackingInt(@intCast(nav_index)), - }; + const uav_index = object_global_index - wasm.object_globals.items.len; + if (is_obj) { + if (uav_index < wasm.uavs_obj.entries.len) { + return .{ .uav_obj = @fromBackingInt(@intCast(uav_index)) }; + } + return .{ .nav_obj = @fromBackingInt( + @intCast(uav_index - wasm.uavs_obj.entries.len), + ) }; + } else { + if (uav_index < wasm.uavs_exe.entries.len) { + return .{ .uav_exe = @fromBackingInt(@intCast(uav_index)) }; + } + return .{ .nav_exe = @fromBackingInt( + @intCast(uav_index - wasm.uavs_exe.entries.len), + ) }; + } }, }; } @@ -1190,11 +1310,29 @@ pub const GlobalImport = extern struct { .__tls_base => .__tls_base, .__tls_size => .__tls_size, .object_global => |i| @fromBackingInt(@intCast(first_object_global + @backingInt(i))), - .nav_obj => |i| @fromBackingInt(@intCast(first_object_global + wasm.object_globals.items.len + @backingInt(i))), - .nav_exe => |i| @fromBackingInt(@intCast(first_object_global + wasm.object_globals.items.len + @backingInt(i))), + inline .uav_obj, .uav_exe => |i| @fromBackingInt(@intCast( + first_object_global + wasm.object_globals.items.len + @backingInt(i), + )), + .nav_obj => |i| @fromBackingInt(@intCast( + first_object_global + wasm.object_globals.items.len + + wasm.uavs_obj.entries.len + @backingInt(i), + )), + .nav_exe => |i| @fromBackingInt(@intCast( + first_object_global + wasm.object_globals.items.len + + wasm.uavs_exe.entries.len + @backingInt(i), + )), }; } + pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { + const is_obj = wasm.base.comp.config.output_mode == .Obj; + return pack(wasm, if (is_obj) .{ + .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), + } else .{ + .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)), + }); + } + pub fn fromIpNav(wasm: *const Wasm, ip_nav: InternPool.Nav.Index) Resolution { const comp = wasm.base.comp; const is_obj = comp.config.output_mode == .Obj; @@ -1209,7 +1347,22 @@ pub const GlobalImport = extern struct { return pack(wasm, .{ .object_global = object_global }); } - pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { + pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { + return switch (unpack(r, wasm)) { + .unresolved, + .__heap_base, + .__heap_end, + .__stack_pointer, + .__tls_align, + .__tls_base, + .__tls_size, + => unreachable, + .object_global => |i| i.ptr(wasm).flags, + .uav_obj, .uav_exe, .nav_obj, .nav_exe => unreachable, + }; + } + + pub fn name(r: Resolution, wasm: *const Wasm, buf: []u8) ?[]const u8 { return switch (unpack(r, wasm)) { .unresolved => unreachable, .__heap_base => @tagName(Unpacked.__heap_base), @@ -1219,6 +1372,11 @@ pub const GlobalImport = extern struct { .__tls_base => @tagName(Unpacked.__tls_base), .__tls_size => @tagName(Unpacked.__tls_size), .object_global => |i| i.name(wasm).slice(wasm), + inline .uav_obj, .uav_exe => |i| std.fmt.bufPrint( + buf, + "__anon_{d}", + .{@backingInt(i.key(wasm).*)}, + ) catch unreachable, .nav_obj => |i| i.name(wasm), .nav_exe => |i| i.name(wasm), }; @@ -1349,6 +1507,22 @@ pub const TableImport = extern struct { return pack(.{ .object_table = object_table }); } + pub fn name(r: Resolution, wasm: *const Wasm) ?[]const u8 { + return switch (unpack(r)) { + .unresolved => unreachable, + .__indirect_function_table => @tagName(Unpacked.__indirect_function_table), + .object_table => |i| i.ptr(wasm).name.slice(wasm), + }; + } + + pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { + return switch (unpack(r)) { + .unresolved => unreachable, + .__indirect_function_table => unreachable, + .object_table => |i| i.ptr(wasm).flags, + }; + } + pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType { return switch (unpack(r)) { .unresolved => unreachable, @@ -1602,6 +1776,8 @@ pub const ObjectDataImport = extern struct { unresolved, __zig_error_names, __zig_error_name_table, + __zig_tag_names, + __zig_tag_name_table, __heap_base, __heap_end, /// Next, an `ObjectData.Index`. @@ -1615,6 +1791,8 @@ pub const ObjectDataImport = extern struct { unresolved, __zig_error_names, __zig_error_name_table, + __zig_tag_names, + __zig_tag_name_table, __heap_base, __heap_end, object: ObjectData.Index, @@ -1629,6 +1807,8 @@ pub const ObjectDataImport = extern struct { .unresolved => .unresolved, .__zig_error_names => .__zig_error_names, .__zig_error_name_table => .__zig_error_name_table, + .__zig_tag_names => .__zig_tag_names, + .__zig_tag_name_table => .__zig_tag_name_table, .__heap_base => .__heap_base, .__heap_end => .__heap_end, _ => { @@ -1665,6 +1845,8 @@ pub const ObjectDataImport = extern struct { .unresolved => .unresolved, .__zig_error_names => .__zig_error_names, .__zig_error_name_table => .__zig_error_name_table, + .__zig_tag_names => .__zig_tag_names, + .__zig_tag_name_table => .__zig_tag_name_table, .__heap_base => .__heap_base, .__heap_end => .__heap_end, .object => |i| @fromBackingInt(@intCast(first_object + @backingInt(i))), @@ -1678,12 +1860,32 @@ pub const ObjectDataImport = extern struct { return pack(wasm, .{ .object = object_data_index }); } + pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) Resolution { + const is_obj = wasm.base.comp.config.output_mode == .Obj; + return pack(wasm, if (is_obj) .{ + .uav_obj = @fromBackingInt(@intCast(wasm.uavs_obj.getIndex(ip_index).?)), + } else .{ + .uav_exe = @fromBackingInt(@intCast(wasm.uavs_exe.getIndex(ip_index).?)), + }); + } + + pub fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) Resolution { + const is_obj = wasm.base.comp.config.output_mode == .Obj; + return pack(wasm, if (is_obj) .{ + .nav_obj = @fromBackingInt(@intCast(wasm.navs_obj.getIndex(nav_index).?)), + } else .{ + .nav_exe = @fromBackingInt(@intCast(wasm.navs_exe.getIndex(nav_index).?)), + }); + } + pub fn objectDataSegment(r: Resolution, wasm: *const Wasm) ?ObjectDataSegment.Index { return switch (unpack(r, wasm)) { .unresolved => unreachable, .object => |i| i.ptr(wasm).segment, .__zig_error_names, .__zig_error_name_table, + .__zig_tag_names, + .__zig_tag_name_table, .__heap_base, .__heap_end, .uav_exe, @@ -1706,12 +1908,107 @@ pub const ObjectDataImport = extern struct { }, .__zig_error_names => .{ .segment = .__zig_error_names, .offset = 0 }, .__zig_error_name_table => .{ .segment = .__zig_error_name_table, .offset = 0 }, + .__zig_tag_names => .{ .segment = .__zig_tag_names, .offset = 0 }, + .__zig_tag_name_table => .{ .segment = .__zig_tag_name_table, .offset = 0 }, .__heap_base => .{ .segment = .__heap_base, .offset = 0 }, .__heap_end => .{ .segment = .__heap_end, .offset = 0 }, - .uav_exe => @panic("TODO"), - .uav_obj => @panic("TODO"), - .nav_exe => @panic("TODO"), - .nav_obj => @panic("TODO"), + .uav_exe => |i| .{ .segment = .pack(wasm, .{ .uav_exe = i }), .offset = 0 }, + .uav_obj => |i| .{ .segment = .pack(wasm, .{ .uav_obj = i }), .offset = 0 }, + .nav_exe => |i| .{ .segment = .pack(wasm, .{ .nav_exe = i }), .offset = 0 }, + .nav_obj => |i| .{ .segment = .pack(wasm, .{ .nav_obj = i }), .offset = 0 }, + }; + } + + pub fn flags(r: Resolution, wasm: *const Wasm) SymbolFlags { + return switch (unpack(r, wasm)) { + .unresolved => unreachable, + .__zig_error_names, + .__zig_error_name_table, + .__zig_tag_names, + .__zig_tag_name_table, + => .{ .binding = .local }, + .__heap_base, + .__heap_end, + => unreachable, + .object => |i| i.ptr(wasm).flags, + inline .nav_exe, .nav_obj => |i| { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(i.key(wasm).*); + if (nav.getExtern(ip)) |ext| { + const name_slice = ext.name.toSlice(ip); + const name_string = wasm.getExistingString(name_slice).?; + return .{ + .binding = switch (ext.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = switch (ext.visibility) { + .default => false, + .hidden => true, + .protected => false, + }, + .undefined = false, + .exported = wasm.missing_exports.contains(name_string), + .explicit_name = false, + .no_strip = false, + .tls = ext.is_threadlocal, + .absolute = false, + }; + } else { + return .{ + .binding = .local, + .tls = nav.resolved.?.@"threadlocal", + }; + } + }, + .uav_exe, .uav_obj => .{ .binding = .local }, + }; + } + + pub fn name(r: Resolution, wasm: *const Wasm, buf: []u8) []const u8 { + return switch (unpack(r, wasm)) { + .unresolved => unreachable, + .object => |i| i.ptr(wasm).name.slice(wasm), + .__zig_error_names => @tagName(.__zig_error_names), + .__zig_error_name_table => @tagName(.__zig_error_name_table), + .__zig_tag_names => @tagName(.__zig_tag_names), + .__zig_tag_name_table => @tagName(.__zig_tag_name_table), + .__heap_base => @tagName(.__heap_base), + .__heap_end => @tagName(.__heap_end), + inline .uav_exe, .uav_obj => |i| std.fmt.bufPrint( + buf, + "__anon_{d}", + .{@backingInt(i.key(wasm).*)}, + ) catch unreachable, + inline .nav_exe, .nav_obj => |i| i.name(wasm), + }; + } + + pub fn size(r: Resolution, wasm: *const Wasm) u32 { + return switch (unpack(r, wasm)) { + .unresolved => unreachable, + .__zig_error_names => @intCast(wasm.error_name_bytes.items.len), + .__zig_error_name_table => { + const comp = wasm.base.comp; + const zcu = comp.zcu.?; + const errors_len = wasm.error_name_offs.items.len; + const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); + return @intCast(errors_len * elem_size); + }, + .__zig_tag_names => @intCast(wasm.tag_name_bytes.items.len), + .__zig_tag_name_table => { + const comp = wasm.base.comp; + const zcu = comp.zcu.?; + const table_len = wasm.tag_name_offs.items.len; + const elem_size = Zcu.Type.slice_const_u8_sentinel_0.abiSize(zcu); + return @intCast(table_len * elem_size); + }, + .__heap_base, .__heap_end => wasm.pointerSize(), + .object => |i| i.ptr(wasm).size, + inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len, }; } }; @@ -1910,6 +2207,38 @@ pub const DataSegmentId = enum(u32) { }; } + pub fn isStrings(id: DataSegmentId, wasm: *const Wasm) bool { + return switch (unpack(id, wasm)) { + .__zig_error_names, .__zig_tag_names => true, + + .__zig_error_name_table, + .__zig_tag_name_table, + .__heap_base, + .__heap_end, + => false, + + .object => |i| i.ptr(wasm).flags.strings, + .uav_exe, .uav_obj => false, + .nav_exe, .nav_obj => false, + }; + } + + pub fn isRetain(id: DataSegmentId, wasm: *const Wasm) bool { + return switch (unpack(id, wasm)) { + .__zig_error_names, + .__zig_error_name_table, + .__zig_tag_names, + .__zig_tag_name_table, + .__heap_base, + .__heap_end, + => false, + + .object => |i| i.ptr(wasm).flags.retain, + .uav_exe, .uav_obj => false, + .nav_exe, .nav_obj => false, + }; + } + pub fn isBss(id: DataSegmentId, wasm: *const Wasm) bool { return id.category(wasm) == .zero; } @@ -2181,6 +2510,7 @@ const PreloadedStrings = struct { _initialize: String, _start: String, memory: String, + env: String, }; /// Index into string_bytes @@ -2262,6 +2592,34 @@ pub const ZcuImportIndex = enum(u32) { return &wasm.imports.keys()[@backingInt(index)]; } + pub fn flags(index: ZcuImportIndex, wasm: *const Wasm) SymbolFlags { + const zcu = wasm.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav_index = index.ptr(wasm).*; + const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern"; + const name_slice = ext.name.toSlice(ip); + const name_string = wasm.getExistingString(name_slice).?; + return .{ + .binding = switch (ext.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = switch (ext.visibility) { + .default => false, + .hidden => true, + .protected => false, + }, + .undefined = true, + .exported = wasm.missing_exports.contains(name_string), + .explicit_name = false, + .no_strip = false, + .tls = ext.is_threadlocal, + .absolute = false, + }; + } + pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String { const zcu = wasm.base.comp.zcu.?; const ip = &zcu.intern_pool; @@ -2348,6 +2706,13 @@ pub const FunctionImportId = enum(u32) { } } + pub fn flags(id: FunctionImportId, wasm: *const Wasm) SymbolFlags { + return switch (id.unpack(wasm)) { + .object_function_import => |i| i.value(wasm).flags, + .zcu_import => |i| i.flags(wasm), + }; + } + pub fn importName(id: FunctionImportId, wasm: *const Wasm) String { return switch (unpack(id, wasm)) { inline .object_function_import, .zcu_import => |i| i.importName(wasm), @@ -2385,38 +2750,61 @@ pub const FunctionImportId = enum(u32) { } }; -/// 0. Index into `object_global_imports`. -/// 1. Index into `imports`. +/// 0. `__stack_pointer`. +/// 1. Index into `object_global_imports`. +/// 2. Index into `imports`. pub const GlobalImportId = enum(u32) { + __stack_pointer, _, pub const Unpacked = union(enum) { + __stack_pointer, object_global_import: GlobalImport.Index, zcu_import: ZcuImportIndex, }; pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId { return switch (unpacked) { - .object_global_import => |i| @fromBackingInt(@intCast(@backingInt(i))), - .zcu_import => |i| @fromBackingInt(@intCast(@backingInt(i) + wasm.object_global_imports.entries.len)), + .__stack_pointer => .__stack_pointer, + .object_global_import => |i| @fromBackingInt(@intCast(@backingInt(i) + 1)), + .zcu_import => |i| @fromBackingInt(@intCast(@backingInt(i) + wasm.object_global_imports.entries.len + 1)), }; } pub fn unpack(id: GlobalImportId, wasm: *const Wasm) Unpacked { - const i = @backingInt(id); - if (i < wasm.object_global_imports.entries.len) return .{ .object_global_import = @fromBackingInt(@intCast(i)) }; - const zcu_import_i = i - wasm.object_global_imports.entries.len; - return .{ .zcu_import = @fromBackingInt(@intCast(zcu_import_i)) }; + return switch (id) { + .__stack_pointer => .__stack_pointer, + _ => { + const i = @backingInt(id) - 1; + if (i < wasm.object_global_imports.entries.len) { + return .{ .object_global_import = @fromBackingInt(@intCast(i)) }; + } + const zcu_import_i = i - wasm.object_global_imports.entries.len; + return .{ .zcu_import = @fromBackingInt(@intCast(zcu_import_i)) }; + }, + }; } pub fn fromObject(object_global_import: GlobalImport.Index, wasm: *const Wasm) GlobalImportId { return pack(.{ .object_global_import = object_global_import }, wasm); } + pub fn flags(id: GlobalImportId, wasm: *const Wasm) SymbolFlags { + return switch (id.unpack(wasm)) { + .__stack_pointer => .{ + .binding = .strong, + .undefined = true, + }, + .object_global_import => |i| i.value(wasm).flags, + .zcu_import => |i| i.flags(wasm), + }; + } + /// This function is allowed O(N) lookup because it is only called during /// diagnostic generation. pub fn sourceLocation(id: GlobalImportId, wasm: *const Wasm) SourceLocation { switch (id.unpack(wasm)) { + .__stack_pointer => return .zig_object_nofile, .object_global_import => |obj_global_index| { // TODO binary search for (wasm.objects.items, 0..) |o, i| { @@ -2433,18 +2821,28 @@ pub const GlobalImportId = enum(u32) { pub fn importName(id: GlobalImportId, wasm: *const Wasm) String { return switch (unpack(id, wasm)) { + .__stack_pointer => wasm.preloaded_strings.__stack_pointer, inline .object_global_import, .zcu_import => |i| i.importName(wasm), }; } pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString { return switch (unpack(id, wasm)) { + .__stack_pointer => wasm.preloaded_strings.env.toOptional(), inline .object_global_import, .zcu_import => |i| i.moduleName(wasm), }; } pub fn globalType(id: GlobalImportId, wasm: *Wasm) ObjectGlobal.Type { return switch (unpack(id, wasm)) { + .__stack_pointer => .{ + .valtype = switch (wasm.pointerSize()) { + 4 => .i32, + 8 => .i64, + else => unreachable, + }, + .mutable = true, + }, inline .object_global_import, .zcu_import => |i| i.globalType(wasm), }; } @@ -2482,6 +2880,13 @@ pub const DataImportId = enum(u32) { return pack(.{ .object_data_import = object_data_import }, wasm); } + pub fn flags(id: DataImportId, wasm: *const Wasm) SymbolFlags { + return switch (id.unpack(wasm)) { + .object_data_import => |i| i.value(wasm).flags, + .zcu_import => |i| i.flags(wasm), + }; + } + pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation { switch (id.unpack(wasm)) { .object_data_import => |obj_data_index| { @@ -2499,33 +2904,42 @@ pub const DataImportId = enum(u32) { } }; -/// Index into `Wasm.symbol_table`. -pub const SymbolTableIndex = enum(u32) { - _, - - pub fn key(i: @This(), wasm: *const Wasm) *String { - return &wasm.symbol_table.keys()[@backingInt(i)]; - } -}; - -pub const OutReloc = struct { +pub const ZcuRelocation = struct { tag: Object.RelocationType, offset: u32, pointee: Pointee, addend: i32, - pub const Pointee = union { - symbol_index: SymbolTableIndex, + pub const Pointee = union(enum) { + function_nav: InternPool.Nav.Index, + function_name: String, + tag_function: InternPool.Index, + data_uav: InternPool.Index, + data_nav: InternPool.Nav.Index, + data_resolution: ObjectDataImport.Resolution, + stack_pointer, type_index: FunctionType.Index, }; pub const Slice = extern struct { - /// Index into `out_relocs`. + /// Index into `zcu_relocations`. off: u32, len: u32, - pub fn slice(s: Slice, wasm: *const Wasm) []OutReloc { - return wasm.relocations.items[s.off..][0..s.len]; + pub fn tags(s: Slice, wasm: *const Wasm) []const Object.RelocationType { + return wasm.zcu_relocations.items(.tag)[s.off..][0..s.len]; + } + + pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 { + return wasm.zcu_relocations.items(.offset)[s.off..][0..s.len]; + } + + pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee { + return wasm.zcu_relocations.items(.pointee)[s.off..][0..s.len]; + } + + pub fn addends(s: Slice, wasm: *const Wasm) []const i32 { + return wasm.zcu_relocations.items(.addend)[s.off..][0..s.len]; } }; }; @@ -3137,9 +3551,9 @@ pub fn deinit(wasm: *Wasm) void { wasm.table_imports.deinit(gpa); wasm.tables.deinit(gpa); wasm.data_imports.deinit(gpa); + wasm.datas.deinit(gpa); wasm.data_segments.deinit(gpa); - wasm.symbol_table.deinit(gpa); - wasm.out_relocs.deinit(gpa); + wasm.zcu_relocations.deinit(gpa); wasm.uav_fixups.deinit(gpa); wasm.nav_fixups.deinit(gpa); wasm.func_table_fixups.deinit(gpa); @@ -3351,6 +3765,25 @@ pub fn updateExports( const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; + const is_obj = wasm.base.comp.config.output_mode == .Obj; + switch (exported) { + .nav => {}, // handled in updateNav + .uav => |uav_index| { // export may be the only reference + const zds: ZcuDataStarts = .init(wasm); + if (is_obj) { + const gop = try wasm.uavs_obj.getOrPut(gpa, uav_index); + if (!gop.found_existing) gop.value_ptr.* = undefined; + } else { + const gop = try wasm.uavs_exe.getOrPut(gpa, uav_index); + if (!gop.found_existing) gop.value_ptr.* = .{ + .code = undefined, + .count = 0, + }; + gop.value_ptr.count += 1; + } + try zds.finish(wasm, pt); + }, + } for (export_indices) |export_idx| { const exp = export_idx.ptr(zcu); const name_slice = exp.opts.name.toSlice(ip); @@ -3443,7 +3876,11 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { // Zig always depends on a stack pointer global. // If emitting an object, it's an import. Otherwise, the linker synthesizes it. if (is_obj) { - @panic("TODO"); + try wasm.global_imports.putNoClobber( + gpa, + wasm.preloaded_strings.__stack_pointer, + .__stack_pointer, + ); } else { try wasm.globals.put(gpa, .__stack_pointer, {}); assert(wasm.globals.entries.len - 1 == @backingInt(GlobalIndex.stack_pointer)); @@ -3453,7 +3890,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { // These loops do both recursive marking of alive symbols well as checking for undefined symbols. // At the end, output functions and globals will be populated. for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| { - if (import.flags.isIncluded(rdynamic)) { + if (import.flags.isIncluded(rdynamic, is_obj)) { try markFunctionImport(wasm, name, import, @fromBackingInt(@intCast(i))); } } @@ -3467,7 +3904,7 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { wasm.functions_end_prelink = @intCast(wasm.functions.entries.len); for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { - if (import.flags.isIncluded(rdynamic)) { + if (import.flags.isIncluded(rdynamic, is_obj)) { try markGlobalImport(wasm, name, import, @fromBackingInt(@intCast(i))); } } @@ -3475,13 +3912,13 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { wasm.global_exports_len = @intCast(wasm.global_exports.items.len); for (wasm.object_table_imports.keys(), wasm.object_table_imports.values(), 0..) |name, *import, i| { - if (import.flags.isIncluded(rdynamic)) { + if (import.flags.isIncluded(rdynamic, is_obj)) { try markTableImport(wasm, name, import, @fromBackingInt(@intCast(i))); } } for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| { - if (import.flags.isIncluded(rdynamic)) { + if (import.flags.isIncluded(rdynamic, is_obj)) { try markDataImport(wasm, name, import, @fromBackingInt(@intCast(i))); } } @@ -3512,18 +3949,23 @@ pub fn markFunctionImport( const comp = wasm.base.comp; const gpa = comp.gpa; + const is_obj = comp.config.output_mode == .Obj; try wasm.functions.ensureUnusedCapacity(gpa, 1); if (import.resolution == .unresolved) { - if (name == wasm.preloaded_strings.__wasm_init_memory) { - try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{}); - } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) { - try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{}); - } else if (name == wasm.preloaded_strings.__wasm_call_ctors) { - try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{}); - } else if (name == wasm.preloaded_strings.__wasm_init_tls) { - try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{}); + if (!is_obj) { + if (name == wasm.preloaded_strings.__wasm_init_memory) { + try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{}); + } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) { + try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{}); + } else if (name == wasm.preloaded_strings.__wasm_call_ctors) { + try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{}); + } else if (name == wasm.preloaded_strings.__wasm_init_tls) { + try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{}); + } else { + try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); + } } else { try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm)); } @@ -3576,28 +4018,33 @@ fn markGlobalImport( const comp = wasm.base.comp; const gpa = comp.gpa; + const is_obj = comp.config.output_mode == .Obj; try wasm.globals.ensureUnusedCapacity(gpa, 1); if (import.resolution == .unresolved) { - if (name == wasm.preloaded_strings.__heap_base) { - import.resolution = .__heap_base; - wasm.globals.putAssumeCapacity(.__heap_base, {}); - } else if (name == wasm.preloaded_strings.__heap_end) { - import.resolution = .__heap_end; - wasm.globals.putAssumeCapacity(.__heap_end, {}); - } else if (name == wasm.preloaded_strings.__stack_pointer) { - import.resolution = .__stack_pointer; - wasm.globals.putAssumeCapacity(.__stack_pointer, {}); - } else if (name == wasm.preloaded_strings.__tls_align) { - import.resolution = .__tls_align; - wasm.globals.putAssumeCapacity(.__tls_align, {}); - } else if (name == wasm.preloaded_strings.__tls_base) { - import.resolution = .__tls_base; - wasm.globals.putAssumeCapacity(.__tls_base, {}); - } else if (name == wasm.preloaded_strings.__tls_size) { - import.resolution = .__tls_size; - wasm.globals.putAssumeCapacity(.__tls_size, {}); + if (!is_obj) { + if (name == wasm.preloaded_strings.__heap_base) { + import.resolution = .__heap_base; + wasm.globals.putAssumeCapacity(.__heap_base, {}); + } else if (name == wasm.preloaded_strings.__heap_end) { + import.resolution = .__heap_end; + wasm.globals.putAssumeCapacity(.__heap_end, {}); + } else if (name == wasm.preloaded_strings.__stack_pointer) { + import.resolution = .__stack_pointer; + wasm.globals.putAssumeCapacity(.__stack_pointer, {}); + } else if (name == wasm.preloaded_strings.__tls_align) { + import.resolution = .__tls_align; + wasm.globals.putAssumeCapacity(.__tls_align, {}); + } else if (name == wasm.preloaded_strings.__tls_base) { + import.resolution = .__tls_base; + wasm.globals.putAssumeCapacity(.__tls_base, {}); + } else if (name == wasm.preloaded_strings.__tls_size) { + import.resolution = .__tls_size; + wasm.globals.putAssumeCapacity(.__tls_size, {}); + } else { + try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); + } } else { try wasm.global_imports.put(gpa, name, .fromObject(global_index, wasm)); } @@ -3625,7 +4072,7 @@ fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Err try wasm.markRelocations(global.relocations(wasm)); } -fn markTableImport( +pub fn markTableImport( wasm: *Wasm, name: String, import: *TableImport, @@ -3636,13 +4083,18 @@ fn markTableImport( const comp = wasm.base.comp; const gpa = comp.gpa; + const is_obj = comp.config.output_mode == .Obj; try wasm.tables.ensureUnusedCapacity(gpa, 1); if (import.resolution == .unresolved) { - if (name == wasm.preloaded_strings.__indirect_function_table) { - import.resolution = .__indirect_function_table; - wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); + if (!is_obj) { + if (name == wasm.preloaded_strings.__indirect_function_table) { + import.resolution = .__indirect_function_table; + wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); + } else { + try wasm.table_imports.put(gpa, name, table_index); + } } else { try wasm.table_imports.put(gpa, name, table_index); } @@ -3676,22 +4128,38 @@ pub fn markDataImport( const comp = wasm.base.comp; const gpa = comp.gpa; + const is_obj = comp.config.output_mode == .Obj; + + try wasm.data_segments.ensureUnusedCapacity(gpa, 1); if (import.resolution == .unresolved) { - if (name == wasm.preloaded_strings.__heap_base) { - import.resolution = .__heap_base; - wasm.data_segments.putAssumeCapacity(.__heap_base, {}); - } else if (name == wasm.preloaded_strings.__heap_end) { - import.resolution = .__heap_end; - wasm.data_segments.putAssumeCapacity(.__heap_end, {}); + if (!is_obj) { + if (name == wasm.preloaded_strings.__heap_base) { + import.resolution = .__heap_base; + wasm.data_segments.putAssumeCapacity(.__heap_base, {}); + } else if (name == wasm.preloaded_strings.__heap_end) { + import.resolution = .__heap_end; + wasm.data_segments.putAssumeCapacity(.__heap_end, {}); + } else { + try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); + } } else { try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm)); } - } else if (import.resolution.objectDataSegment(wasm)) |segment_index| { - try markDataSegment(wasm, segment_index); + } else switch (import.resolution.unpack(wasm)) { + .object => |object_data_index| try markData(wasm, object_data_index), + else => {}, } } +fn markData(wasm: *Wasm, i: ObjectData.Index) link.Error!void { + const gpa = wasm.base.comp.gpa; + const gop = try wasm.datas.getOrPut(gpa, .fromObjectDataIndex(wasm, i)); + if (gop.found_existing) return; + + try markDataSegment(wasm, i.ptr(wasm).segment); +} + fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Error!void { const gpa = wasm.base.comp.gpa; for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| { @@ -3782,7 +4250,7 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Err .memory_addr_tls_sleb, .memory_addr_locrel_i32, .memory_addr_tls_sleb64, - => try markDataSegment(wasm, pointee.data.ptr(wasm).segment), + => try markData(wasm, pointee.data), .type_index_leb => continue, } @@ -3829,7 +4297,13 @@ pub fn flush( const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len); defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu); + const global_exports_end_zcu: u32 = @intCast(wasm.global_exports.items.len); + defer wasm.global_exports.shrinkRetainingCapacity(global_exports_end_zcu); + wasm.flush_buffer.clear(); + wasm.tag_name_bytes.clearRetainingCapacity(); + wasm.tag_name_offs.clearRetainingCapacity(); + wasm.tag_name_table_ref_count = 0; try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values()); @@ -3953,6 +4427,255 @@ pub fn getExistingFunctionType( }); } +fn internIntrinsicType( + wasm: *Wasm, + params: []const InternPool.Index, + return_type: Zcu.Type, +) Allocator.Error!FunctionType.Index { + const target = &wasm.base.comp.root_mod.resolved_target.result; + return wasm.internFunctionType(.{ .wasm_mvp = .{} }, params, return_type, false, target); +} + +pub fn intrinsicFunctionType(wasm: *Wasm, intrinsic: Mir.Intrinsic) Allocator.Error!FunctionType.Index { + return switch (intrinsic) { + .__addhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__addtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .__addxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__ashlti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), + .__ashrti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), + .__bitreversedi2 => internIntrinsicType(wasm, &.{.u64_type}, .u64), + .__bitreversesi2 => internIntrinsicType(wasm, &.{.u32_type}, .u32), + .__bswapdi2 => internIntrinsicType(wasm, &.{.u64_type}, .u64), + .__bswapsi2 => internIntrinsicType(wasm, &.{.u32_type}, .u32), + .__ceilh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__ceilx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__cosh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__cosx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__divei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), + .__divhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__divtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .__divti3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), + .__divxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__eqtf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__eqxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__exp2h => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__exp2x => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__exph => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__expx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__extenddftf2 => internIntrinsicType(wasm, &.{.f64_type}, .f128), + .__extenddfxf2 => internIntrinsicType(wasm, &.{.f64_type}, .f80), + .__extendhfsf2 => internIntrinsicType(wasm, &.{.f16_type}, .f32), + .__extendhftf2 => internIntrinsicType(wasm, &.{.f16_type}, .f128), + .__extendhfxf2 => internIntrinsicType(wasm, &.{.f16_type}, .f80), + .__extendsftf2 => internIntrinsicType(wasm, &.{.f32_type}, .f128), + .__extendsfxf2 => internIntrinsicType(wasm, &.{.f32_type}, .f80), + .__extendxftf2 => internIntrinsicType(wasm, &.{.f80_type}, .f128), + .__fabsh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__fabsx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__fixdfdi => internIntrinsicType(wasm, &.{.f64_type}, .i64), + .__fixdfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f64_type }, .void), + .__fixdfsi => internIntrinsicType(wasm, &.{.f64_type}, .i32), + .__fixdfti => internIntrinsicType(wasm, &.{.f64_type}, .i128), + .__fixhfdi => internIntrinsicType(wasm, &.{.f16_type}, .i64), + .__fixhfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f16_type }, .void), + .__fixhfsi => internIntrinsicType(wasm, &.{.f16_type}, .i32), + .__fixhfti => internIntrinsicType(wasm, &.{.f16_type}, .i128), + .__fixsfdi => internIntrinsicType(wasm, &.{.f32_type}, .i64), + .__fixsfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f32_type }, .void), + .__fixsfsi => internIntrinsicType(wasm, &.{.f32_type}, .i32), + .__fixsfti => internIntrinsicType(wasm, &.{.f32_type}, .i128), + .__fixtfdi => internIntrinsicType(wasm, &.{.f128_type}, .i64), + .__fixtfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f128_type }, .void), + .__fixtfsi => internIntrinsicType(wasm, &.{.f128_type}, .i32), + .__fixtfti => internIntrinsicType(wasm, &.{.f128_type}, .i128), + .__fixunsdfdi => internIntrinsicType(wasm, &.{.f64_type}, .u64), + .__fixunsdfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f64_type }, .void), + .__fixunsdfsi => internIntrinsicType(wasm, &.{.f64_type}, .u32), + .__fixunsdfti => internIntrinsicType(wasm, &.{.f64_type}, .u128), + .__fixunshfdi => internIntrinsicType(wasm, &.{.f16_type}, .u64), + .__fixunshfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f16_type }, .void), + .__fixunshfsi => internIntrinsicType(wasm, &.{.f16_type}, .u32), + .__fixunshfti => internIntrinsicType(wasm, &.{.f16_type}, .u128), + .__fixunssfdi => internIntrinsicType(wasm, &.{.f32_type}, .u64), + .__fixunssfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f32_type }, .void), + .__fixunssfsi => internIntrinsicType(wasm, &.{.f32_type}, .u32), + .__fixunssfti => internIntrinsicType(wasm, &.{.f32_type}, .u128), + .__fixunstfdi => internIntrinsicType(wasm, &.{.f128_type}, .u64), + .__fixunstfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f128_type }, .void), + .__fixunstfsi => internIntrinsicType(wasm, &.{.f128_type}, .u32), + .__fixunstfti => internIntrinsicType(wasm, &.{.f128_type}, .u128), + .__fixunsxfdi => internIntrinsicType(wasm, &.{.f80_type}, .u64), + .__fixunsxfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f80_type }, .void), + .__fixunsxfsi => internIntrinsicType(wasm, &.{.f80_type}, .u32), + .__fixunsxfti => internIntrinsicType(wasm, &.{.f80_type}, .u128), + .__fixxfdi => internIntrinsicType(wasm, &.{.f80_type}, .i64), + .__fixxfei => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .f80_type }, .void), + .__fixxfsi => internIntrinsicType(wasm, &.{.f80_type}, .i32), + .__fixxfti => internIntrinsicType(wasm, &.{.f80_type}, .i128), + .__floatdidf => internIntrinsicType(wasm, &.{.i64_type}, .f64), + .__floatdihf => internIntrinsicType(wasm, &.{.i64_type}, .f16), + .__floatdisf => internIntrinsicType(wasm, &.{.i64_type}, .f32), + .__floatditf => internIntrinsicType(wasm, &.{.i64_type}, .f128), + .__floatdixf => internIntrinsicType(wasm, &.{.i64_type}, .f80), + .__floateidf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f64), + .__floateihf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f16), + .__floateisf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f32), + .__floateitf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f128), + .__floateixf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f80), + .__floatsidf => internIntrinsicType(wasm, &.{.i32_type}, .f64), + .__floatsihf => internIntrinsicType(wasm, &.{.i32_type}, .f16), + .__floatsisf => internIntrinsicType(wasm, &.{.i32_type}, .f32), + .__floatsitf => internIntrinsicType(wasm, &.{.i32_type}, .f128), + .__floatsixf => internIntrinsicType(wasm, &.{.i32_type}, .f80), + .__floattidf => internIntrinsicType(wasm, &.{.i128_type}, .f64), + .__floattihf => internIntrinsicType(wasm, &.{.i128_type}, .f16), + .__floattisf => internIntrinsicType(wasm, &.{.i128_type}, .f32), + .__floattitf => internIntrinsicType(wasm, &.{.i128_type}, .f128), + .__floattixf => internIntrinsicType(wasm, &.{.i128_type}, .f80), + .__floatundidf => internIntrinsicType(wasm, &.{.u64_type}, .f64), + .__floatundihf => internIntrinsicType(wasm, &.{.u64_type}, .f16), + .__floatundisf => internIntrinsicType(wasm, &.{.u64_type}, .f32), + .__floatunditf => internIntrinsicType(wasm, &.{.u64_type}, .f128), + .__floatundixf => internIntrinsicType(wasm, &.{.u64_type}, .f80), + .__floatuneidf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f64), + .__floatuneihf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f16), + .__floatuneisf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f32), + .__floatuneitf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f128), + .__floatuneixf => internIntrinsicType(wasm, &.{ .usize_type, .usize_type }, .f80), + .__floatunsidf => internIntrinsicType(wasm, &.{.u32_type}, .f64), + .__floatunsihf => internIntrinsicType(wasm, &.{.u32_type}, .f16), + .__floatunsisf => internIntrinsicType(wasm, &.{.u32_type}, .f32), + .__floatunsitf => internIntrinsicType(wasm, &.{.u32_type}, .f128), + .__floatunsixf => internIntrinsicType(wasm, &.{.u32_type}, .f80), + .__floatuntidf => internIntrinsicType(wasm, &.{.u128_type}, .f64), + .__floatuntihf => internIntrinsicType(wasm, &.{.u128_type}, .f16), + .__floatuntisf => internIntrinsicType(wasm, &.{.u128_type}, .f32), + .__floatuntitf => internIntrinsicType(wasm, &.{.u128_type}, .f128), + .__floatuntixf => internIntrinsicType(wasm, &.{.u128_type}, .f80), + .__floorh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__floorx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__fmah => internIntrinsicType(wasm, &.{ .f16_type, .f16_type, .f16_type }, .f16), + .__fmax => internIntrinsicType(wasm, &.{ .f80_type, .f80_type, .f80_type }, .f80), + .__fmaxh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__fmaxx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__fminh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__fminx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__fmodh => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__fmodx => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__getf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__gexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__gttf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__gtxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__letf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__lexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__log10h => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__log10x => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__log2h => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__log2x => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__logh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__logx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__lshrti3 => internIntrinsicType(wasm, &.{ .i128_type, .i32_type }, .i128), + .__lttf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__ltxf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__modei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), + .__modti3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), + .__mulhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__mulodi4 => internIntrinsicType(wasm, &.{ .i64_type, .i64_type, .usize_type }, .i64), + .__muloti4 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type, .usize_type }, .i128), + .__multf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .__multi3 => internIntrinsicType(wasm, &.{ .i128_type, .i128_type }, .i128), + .__mulxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__netf2 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .bool), + .__nexf2 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .bool), + .__roundh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__roundx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__sinh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__sinx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__sqrth => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__sqrtx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__subhf3 => internIntrinsicType(wasm, &.{ .f16_type, .f16_type }, .f16), + .__subtf3 => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .__subxf3 => internIntrinsicType(wasm, &.{ .f80_type, .f80_type }, .f80), + .__tanh => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__tanx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__trunch => internIntrinsicType(wasm, &.{.f16_type}, .f16), + .__truncsfhf2 => internIntrinsicType(wasm, &.{.f32_type}, .f16), + .__trunctfdf2 => internIntrinsicType(wasm, &.{.f128_type}, .f64), + .__trunctfhf2 => internIntrinsicType(wasm, &.{.f128_type}, .f16), + .__trunctfsf2 => internIntrinsicType(wasm, &.{.f128_type}, .f32), + .__trunctfxf2 => internIntrinsicType(wasm, &.{.f128_type}, .f80), + .__truncx => internIntrinsicType(wasm, &.{.f80_type}, .f80), + .__truncxfdf2 => internIntrinsicType(wasm, &.{.f80_type}, .f64), + .__truncxfhf2 => internIntrinsicType(wasm, &.{.f80_type}, .f16), + .__truncxfsf2 => internIntrinsicType(wasm, &.{.f80_type}, .f32), + .__udivei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), + .__udivti3 => internIntrinsicType(wasm, &.{ .u128_type, .u128_type }, .u128), + .__umodei5 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, .void), + .__umodti3 => internIntrinsicType(wasm, &.{ .u128_type, .u128_type }, .u128), + .ceilq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .cos => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .cosf => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .cosq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .exp => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .exp2 => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .exp2f => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .exp2q => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .expf => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .expq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .fabsq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .floorq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .fma => internIntrinsicType(wasm, &.{ .f64_type, .f64_type, .f64_type }, .f64), + .fmaf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type, .f32_type }, .f32), + .fmaq => internIntrinsicType(wasm, &.{ .f128_type, .f128_type, .f128_type }, .f128), + .fmax => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), + .fmaxf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), + .fmaxq => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .fmin => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), + .fminf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), + .fminq => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .fmod => internIntrinsicType(wasm, &.{ .f64_type, .f64_type }, .f64), + .fmodf => internIntrinsicType(wasm, &.{ .f32_type, .f32_type }, .f32), + .fmodq => internIntrinsicType(wasm, &.{ .f128_type, .f128_type }, .f128), + .log => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .log10 => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .log10f => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .log10q => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .log2 => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .log2f => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .log2q => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .logf => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .logq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .roundq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .sin => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .sinf => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .sinq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .sqrtq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .tan => internIntrinsicType(wasm, &.{.f64_type}, .f64), + .tanf => internIntrinsicType(wasm, &.{.f32_type}, .f32), + .tanq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .truncq => internIntrinsicType(wasm, &.{.f128_type}, .f128), + .memcpy => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type }, .usize), + .memmove => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type }, .usize), + .memset => internIntrinsicType(wasm, &.{ .usize_type, .i32_type, .usize_type }, .usize), + .__addo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), + .__subo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), + .__cmp_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .i8), + .__and_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), + .__or_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), + .__xor_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .u16_type }, .void), + .__not_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), + .__shlo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type, .bool_type, .u16_type }, .bool), + .__shr_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type, .bool_type, .u16_type }, .void), + .__clz_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), + .__ctz_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), + .__popcount_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .u16_type }, .u16), + .__bitreverse_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), + .__byteswap_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .bool_type, .u16_type }, .void), + .__mulo_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .usize_type, .bool_type, .u16_type }, .bool), + .__abs_limb64 => internIntrinsicType(wasm, &.{ .usize_type, .usize_type, .u16_type }, .void), + }; +} + pub fn addExpr(wasm: *Wasm, bytes: []const u8) Allocator.Error!Expr { const gpa = wasm.base.comp.gpa; // We can't use string table deduplication here since these expressions can @@ -3972,64 +4695,63 @@ pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error }; } -pub fn uavSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { +pub fn addNavReloc( + wasm: *Wasm, + reloc_offset: usize, + nav_index: InternPool.Nav.Index, + nav_ty: Zcu.Type, + addend: u32, +) !void { const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); - const gpa = comp.gpa; - const name = try wasm.internStringFmt("__anon_{d}", .{@backingInt(ip_index)}); - const gop = try wasm.symbol_table.getOrPut(gpa, name); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); -} - -pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!SymbolTableIndex { - const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); const zcu = comp.zcu.?; const ip = &zcu.intern_pool; const gpa = comp.gpa; - const nav = ip.getNav(nav_index); - const name = try wasm.internString(nav.fqn.toSlice(ip)); - const gop = try wasm.symbol_table.getOrPut(gpa, name); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); -} -pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { - const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); - const gpa = comp.gpa; - const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__zig_error_name_table); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); -} + const is_obj = comp.config.output_mode == .Obj; -pub fn stackPointerSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex { - const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); - const gpa = comp.gpa; - const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__stack_pointer); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); -} - -pub fn tagTableIndexSymbolIndex(wasm: *Wasm, ip_index: InternPool.Index) Allocator.Error!SymbolTableIndex { - const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); - const gpa = comp.gpa; - const name = try wasm.internStringFmt("__zig_tag_name_{d}", .{ip_index}); - const gop = try wasm.symbol_table.getOrPut(gpa, name); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); -} - -pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableIndex { - const comp = wasm.base.comp; - assert(comp.config.output_mode == .Obj); - const gpa = comp.gpa; - const gop = try wasm.symbol_table.getOrPut(gpa, name); - gop.value_ptr.* = {}; - return @fromBackingInt(@intCast(gop.index)); + if (nav_ty.zigTypeTag(zcu) == .@"fn") { + const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); + if (!gop.found_existing) gop.value_ptr.* = {}; + if (is_obj) { + assert(addend == 0); + try wasm.zcu_relocations.append(gpa, .{ + .offset = @intCast(reloc_offset), + .pointee = .{ .function_nav = nav_index }, + .tag = switch (wasm.pointerSize()) { + 4 => .table_index_i32, + 8 => .table_index_i64, + else => unreachable, + }, + .addend = 0, + }); + } else { + try wasm.func_table_fixups.append(gpa, .{ + .nav_index = nav_index, + .offset = @intCast(reloc_offset), + }); + } + } else { + if (is_obj) { + if (ip.getNav(nav_index).getExtern(ip) == null) _ = try wasm.refNavObj(nav_index); + try wasm.zcu_relocations.append(gpa, .{ + .offset = @intCast(reloc_offset), + .pointee = .{ .data_nav = nav_index }, + .tag = switch (wasm.pointerSize()) { + 4 => .memory_addr_i32, + 8 => .memory_addr_i64, + else => unreachable, + }, + .addend = @intCast(addend), + }); + } else { + try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1); + wasm.nav_fixups.appendAssumeCapacity(.{ + .nav_index = nav_index, + .offset = @intCast(reloc_offset), + .addend = addend, + }); + } + } } pub fn addUavReloc( @@ -4057,12 +4779,12 @@ pub fn addUavReloc( if (comp.config.output_mode == .Obj) { const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later - try wasm.out_relocs.append(gpa, .{ + try wasm.zcu_relocations.append(gpa, .{ .offset = @intCast(reloc_offset), - .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav_val) }, + .pointee = .{ .data_uav = uav_val }, .tag = switch (wasm.pointerSize()) { - 32 => .memory_addr_i32, - 64 => .memory_addr_i64, + 4 => .memory_addr_i32, + 8 => .memory_addr_i64, else => unreachable, }, .addend = @intCast(addend), @@ -4085,7 +4807,7 @@ pub fn addUavReloc( pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { const comp = wasm.base.comp; const gpa = comp.gpa; - assert(comp.config.output_mode != .Obj); + assert(comp.config.output_mode == .Obj); const gop = try wasm.navs_obj.getOrPut(gpa, nav_index); if (!gop.found_existing) gop.value_ptr.* = .{ // Lowering the value is delayed to avoid recursion. @@ -4113,7 +4835,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { } /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. -pub fn uavAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { +pub fn uavAddr(wasm: *const Wasm, ip_index: InternPool.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); const comp = wasm.base.comp; assert(comp.config.output_mode != .Obj); @@ -4123,7 +4845,7 @@ pub fn uavAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { } /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. -pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 { +pub fn navAddr(wasm: *const Wasm, nav_index: InternPool.Nav.Index) u32 { assert(wasm.flush_buffer.memory_layout_finished); const comp = wasm.base.comp; assert(comp.config.output_mode != .Obj); @@ -4139,23 +4861,34 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 { .@"extern" => |ext| if (wasm.getExistingString(ext.name.toSlice(ip))) |symbol_name| { if (wasm.object_data_imports.getPtr(symbol_name)) |import| { switch (import.resolution.unpack(wasm)) { - .unresolved => unreachable, + .unresolved => {}, .object => |object_data_index| { const object_data = object_data_index.ptr(wasm); const ds_id: DataSegmentId = .fromObjectDataSegment(wasm, object_data.segment); const segment_base_addr = wasm.flush_buffer.data_segments.get(ds_id).?; return segment_base_addr + object_data.offset; }, - .__zig_error_names => @panic("TODO"), - .__zig_error_name_table => @panic("TODO"), - .__heap_base => @panic("TODO"), - .__heap_end => @panic("TODO"), - .uav_exe => @panic("TODO"), - .uav_obj => @panic("TODO"), - .nav_exe => @panic("TODO"), - .nav_obj => @panic("TODO"), + .__heap_base, + .__heap_end, + .uav_exe, + .nav_exe, + => { + const data_loc = import.resolution.dataLoc(wasm); + return wasm.flush_buffer.data_segments.get(data_loc.segment).? + data_loc.offset; + }, + .__zig_error_names, + .__zig_error_name_table, + .__zig_tag_names, + .__zig_tag_name_table, + .uav_obj, + .nav_obj, + => unreachable, } } + if (wasm.flush_buffer.data_exports.get(symbol_name)) |symbol| { + const data_loc = symbol.resolution.dataLoc(wasm); + return wasm.flush_buffer.data_segments.get(data_loc.segment).? + data_loc.offset; + } }, else => {}, } @@ -4177,8 +4910,12 @@ pub fn tagIndexTableAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { assert(comp.config.output_mode != .Obj); const f = &wasm.flush_buffer; const table_base_addr = f.data_segments.get(.__zig_tag_name_table).?; - const table_index = f.enum_tag_name_table.get(ip_index).?; - return table_base_addr + table_index * 8; + return table_base_addr + wasm.tagIndexTableOffset(ip_index); +} + +pub fn tagIndexTableOffset(wasm: *const Wasm, ip_index: InternPool.Index) u32 { + const table_index = wasm.flush_buffer.enum_tag_name_table.get(ip_index).?; + return table_index * wasm.pointerSize() * 2; } fn convertZcuFnType( @@ -4255,7 +4992,7 @@ pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool { /// those entries. fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj { const code_start: u32 = @intCast(wasm.string_bytes.items.len); - const relocs_start: u32 = @intCast(wasm.out_relocs.len); + const relocs_start: u32 = @intCast(wasm.zcu_relocations.len); const uav_fixups_start: u32 = @intCast(wasm.uav_fixups.items.len); const nav_fixups_start: u32 = @intCast(wasm.nav_fixups.items.len); const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len); @@ -4271,8 +5008,9 @@ fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Zcu } const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start); - const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start); + const relocs_len: u32 = @intCast(wasm.zcu_relocations.len - relocs_start); const any_fixups = + relocs_len != 0 or uav_fixups_start != wasm.uav_fixups.items.len or nav_fixups_start != wasm.nav_fixups.items.len or func_table_fixups_start != wasm.func_table_fixups.items.len; diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index ac580f9bbc5656362185489f457f15b387605a68..45633149611c318bb5623d6cbb5ba75bcab77f5a 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -7,7 +7,6 @@ const Object = @import("Object.zig"); const Zcu = @import("../../Zcu.zig"); const Alignment = Wasm.Alignment; const String = Wasm.String; -const Relocation = Wasm.Relocation; const InternPool = @import("../../InternPool.zig"); const Mir = @import("../../codegen/wasm/Mir.zig"); @@ -33,8 +32,13 @@ data_segment_groups: ArrayList(DataSegmentGroup) = .empty, binary_bytes: ArrayList(u8) = .empty, missing_exports: std.array_hash_map.Auto(String, void) = .empty, function_imports: std.array_hash_map.Auto(String, Wasm.FunctionImportId) = .empty, +intrinsic_function_imports: std.array_hash_map.Auto(String, Wasm.FunctionType.Index) = .empty, +/// Function aliases emitted after function symbols. +function_export_symbols: std.array_hash_map.Auto(String, FunctionExportSymbol) = .empty, global_imports: std.array_hash_map.Auto(String, Wasm.GlobalImportId) = .empty, data_imports: std.array_hash_map.Auto(String, Wasm.DataImportId) = .empty, +/// Data aliases emitted after data symbols. +data_exports: std.array_hash_map.Auto(String, DataExportSymbol) = .empty, indirect_function_table: std.array_hash_map.Auto(Wasm.OutputFunctionIndex, void) = .empty, @@ -43,6 +47,9 @@ func_types: std.array_hash_map.Auto(Wasm.FunctionType.Index, void) = .empty, enum_tag_name_table: std.array_hash_map.Auto(InternPool.Index, u32) = .empty, +code_relocs: std.ArrayList(Relocation) = .empty, +data_relocs: std.ArrayList(Relocation) = .empty, + /// For debug purposes only. memory_layout_finished: bool = false, @@ -55,6 +62,74 @@ pub const FuncTypeIndex = enum(u32) { } }; +/// Index into SYMTAB_FUNCTION. +const FunctionSymbolIndex = enum(u32) { + _, + + fn fromOutputFunctionIndex(i: Wasm.OutputFunctionIndex) FunctionSymbolIndex { + return @fromBackingInt(@backingInt(i)); + } + + fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: Wasm.ObjectFunctionIndex) FunctionSymbolIndex { + return fromOutputFunctionIndex(.fromObjectFunctionHandlingWeak(wasm, index)); + } + + fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) FunctionSymbolIndex { + return fromOutputFunctionIndex(.fromIpNav(wasm, nav_index)); + } + + fn fromTagIndexType(wasm: *const Wasm, ip_index: InternPool.Index) FunctionSymbolIndex { + return fromOutputFunctionIndex(.fromTagIndexType(wasm, ip_index)); + } + + fn fromSymbolName(wasm: *const Wasm, name: String) FunctionSymbolIndex { + const f = &wasm.flush_buffer; + if (f.function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); + if (f.intrinsic_function_imports.getIndex(name)) |i| return @fromBackingInt(@intCast( + f.function_imports.entries.len + i, + )); + if (f.function_export_symbols.getIndex(name)) |i| return @fromBackingInt(@intCast( + f.function_imports.entries.len + f.intrinsic_function_imports.entries.len + + wasm.functions.entries.len + i, + )); + return fromOutputFunctionIndex(.fromSymbolName(wasm, name)); + } +}; + +/// Index into SYMTAB_DATA. +const DataSymbolIndex = enum(u32) { + _, + + fn fromOutputDataIndex(i: Wasm.OutputDataIndex) DataSymbolIndex { + return @fromBackingInt(@backingInt(i)); + } + + fn fromResolution(wasm: *const Wasm, resolution: Wasm.ObjectDataImport.Resolution) DataSymbolIndex { + return fromOutputDataIndex(Wasm.OutputDataIndex.fromResolution(wasm, resolution).?); + } + + fn fromObjectData(wasm: *const Wasm, index: Wasm.ObjectData.Index) DataSymbolIndex { + return fromOutputDataIndex(.fromObjectData(wasm, index)); + } + + fn fromUav(wasm: *const Wasm, ip_index: InternPool.Index) DataSymbolIndex { + return fromOutputDataIndex(.fromUav(wasm, ip_index)); + } + + fn fromNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) DataSymbolIndex { + return fromOutputDataIndex(.fromNav(wasm, nav_index)); + } + + fn fromSymbolName(wasm: *const Wasm, name: String) DataSymbolIndex { + const f = &wasm.flush_buffer; + if (f.data_imports.getIndex(name)) |i| return @fromBackingInt(@intCast(i)); + if (f.data_exports.getIndex(name)) |i| return @fromBackingInt(@intCast( + f.data_imports.entries.len + wasm.datas.entries.len + i, + )); + return fromOutputDataIndex(.fromSymbolName(wasm, name)); + } +}; + /// Index into `indirect_function_table`. const IndirectFunctionTableIndex = enum(u32) { _, @@ -71,9 +146,8 @@ const IndirectFunctionTableIndex = enum(u32) { return @fromBackingInt(@intCast(f.indirect_function_table.getIndex(i).?)); } - fn fromZcuIndirectFunctionSetIndex(i: Wasm.ZcuIndirectFunctionSetIndex) IndirectFunctionTableIndex { - // These are the same since those are added to the table first. - return @fromBackingInt(@intCast(@backingInt(i))); + fn fromIpNav(wasm: *const Wasm, nav_index: InternPool.Nav.Index) IndirectFunctionTableIndex { + return fromOutputFunctionIndex(&wasm.flush_buffer, .fromIpNav(wasm, nav_index)); } fn toAbi(i: IndirectFunctionTableIndex) u32 { @@ -81,6 +155,39 @@ const IndirectFunctionTableIndex = enum(u32) { } }; +const SymbolTableOffsets = struct { + function: u32, + data: u32, + global: u32, + table: u32, +}; + +const FunctionExportSymbol = struct { + function_index: Wasm.FunctionIndex, + flags: Wasm.SymbolFlags, +}; + +const DataExportSymbol = struct { + resolution: Wasm.ObjectDataImport.Resolution, + flags: Wasm.SymbolFlags, +}; + +const Relocation = struct { + tag: Object.RelocationType, + offset: u32, + pointee: Pointee, + addend: i32, + + const Pointee = union { + data: DataSymbolIndex, + type_index: FuncTypeIndex, + section: Wasm.ObjectSectionIndex, + function: FunctionSymbolIndex, + global: Wasm.GlobalIndex, + table: Wasm.TableIndex, + }; +}; + const DataSegmentGroup = struct { first_segment: Wasm.DataSegmentId, end_addr: u32, @@ -90,9 +197,14 @@ pub fn clear(f: *Flush) void { f.data_segments.clearRetainingCapacity(); f.data_segment_groups.clearRetainingCapacity(); f.binary_bytes.clearRetainingCapacity(); + f.intrinsic_function_imports.clearRetainingCapacity(); + f.function_export_symbols.clearRetainingCapacity(); + f.data_exports.clearRetainingCapacity(); f.indirect_function_table.clearRetainingCapacity(); f.func_types.clearRetainingCapacity(); f.enum_tag_name_table.clearRetainingCapacity(); + f.code_relocs.clearRetainingCapacity(); + f.data_relocs.clearRetainingCapacity(); f.memory_layout_finished = false; } @@ -102,11 +214,16 @@ pub fn deinit(f: *Flush, gpa: Allocator) void { f.binary_bytes.deinit(gpa); f.missing_exports.deinit(gpa); f.function_imports.deinit(gpa); + f.intrinsic_function_imports.deinit(gpa); + f.function_export_symbols.deinit(gpa); f.global_imports.deinit(gpa); f.data_imports.deinit(gpa); + f.data_exports.deinit(gpa); f.indirect_function_table.deinit(gpa); f.func_types.deinit(gpa); f.enum_tag_name_table.deinit(gpa); + f.code_relocs.deinit(gpa); + f.data_relocs.deinit(gpa); f.* = undefined; } @@ -134,48 +251,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { log.debug("total MIR instructions: {d}", .{wasm.mir_instructions.len}); - // Detect any intrinsics that were called; they need to have dependencies on the symbols marked. - // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized. - for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) { - .call_intrinsic => { - const symbol_name = try wasm.internString(@tagName(data.intrinsic)); - const i: Wasm.FunctionImport.Index = @fromBackingInt(@intCast(wasm.object_function_imports.getIndex(symbol_name) orelse { - return diags.fail("missing compiler runtime intrinsic '{t}' (undefined linker symbol)", .{ - data.intrinsic, - }); - })); - try wasm.markFunctionImport(symbol_name, i.value(wasm), i); - log.debug("markFunctionImport intrinsic {d}={t}", .{ i, data.intrinsic }); - }, - .call_tag_index => { - assert(ip.indexToKey(data.ip_index) == .enum_type); - const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index); - if (!gop.found_existing) { - const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).backingIntType(zcu); - gop.value_ptr.* = .{ .tag_name = .{ - .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}), - .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target), - } }; - } - try wasm.functions.put(gpa, .fromZcuFunc(wasm, @fromBackingInt(@intCast(gop.index))), {}); - }, - .enum_tag_name_table_ref => { - assert(ip.indexToKey(data.ip_index) == .enum_type); - const gop = try f.enum_tag_name_table.getOrPut(gpa, data.ip_index); - if (!gop.found_existing) { - wasm.tag_name_table_ref_count += 1; - gop.value_ptr.* = @intCast(wasm.tag_name_offs.items.len); - const tag_names = ip.loadEnumType(data.ip_index).field_names; - for (tag_names.get(ip)) |tag_name| { - const slice = tag_name.toSlice(ip); - try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len)); - try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); - } - } - }, - else => continue, - }; - { var i = wasm.function_imports_len_prelink; while (i < f.function_imports.entries.len) { @@ -225,10 +300,24 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index }); const function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?; const explicit = f.missing_exports.swapRemove(nav_export.name); - const is_hidden = !explicit and switch (export_index.ptr(zcu).opts.visibility) { + const opts = export_index.ptr(zcu).opts; + const is_hidden = !explicit and switch (opts.visibility) { .hidden => true, .default, .protected => false, }; + if (is_obj) try f.function_export_symbols.put(gpa, nav_export.name, .{ + .function_index = function_index, + .flags = .{ + .binding = switch (opts.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = is_hidden, + .exported = !is_hidden, + }, + }); if (is_hidden) { try wasm.hidden_function_exports.put(gpa, nav_export.name, function_index); } else { @@ -239,17 +328,141 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (nav_export.name.toOptional() == entry_name) wasm.entry_resolution = .fromIpNav(wasm, nav_export.nav_index); } else { - // This is a data export because Zcu currently has no way to - // export wasm globals. - _ = f.missing_exports.swapRemove(nav_export.name); + // data exports are linker symbols + // explicit exports become address globals + const explicit = f.missing_exports.swapRemove(nav_export.name); + const opts = export_index.ptr(zcu).opts; + try f.data_exports.put(gpa, nav_export.name, .{ + .resolution = .fromIpNav(wasm, nav_export.nav_index), + .flags = if (is_obj) .{ + .binding = switch (opts.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = !explicit and switch (opts.visibility) { + .default => false, + .hidden => true, + .protected => false, + }, + .exported = explicit, + .tls = ip.getNav(nav_export.nav_index).resolved.?.@"threadlocal", + } else .{}, + }); _ = f.data_imports.swapRemove(nav_export.name); - if (!is_obj) { - diags.addError("unable to export data symbol '{s}'; not emitting a relocatable", .{ - nav_export.name.slice(wasm), + if (explicit and !is_obj) { + const global_resolution: Wasm.GlobalImport.Resolution = .fromIpNav( + wasm, + nav_export.nav_index, + ); + try wasm.globals.put(gpa, global_resolution, {}); + try wasm.global_exports.append(gpa, .{ + .name = nav_export.name, + .global_index = Wasm.GlobalIndex.fromResolution(wasm, global_resolution).?, }); } } } + // handle exported values without navs + for (wasm.uav_exports.keys(), wasm.uav_exports.values()) |uav_export, export_index| { + assert(!ip.isFunctionType(ip.typeOf(uav_export.uav_index))); + const explicit = f.missing_exports.swapRemove(uav_export.name); + const opts = export_index.ptr(zcu).opts; + try f.data_exports.put(gpa, uav_export.name, .{ + .resolution = .fromIpIndex(wasm, uav_export.uav_index), + .flags = if (is_obj) .{ + .binding = switch (opts.linkage) { + .internal => .local, + .strong => .strong, + .weak => .weak, + .link_once => @panic("TODO: COMDAT"), + }, + .visibility_hidden = !explicit and switch (opts.visibility) { + .default => false, + .hidden => true, + .protected => false, + }, + .exported = explicit, + } else .{}, + }); + _ = f.data_imports.swapRemove(uav_export.name); + if (explicit and !is_obj) { + const global_resolution: Wasm.GlobalImport.Resolution = .fromIpIndex( + wasm, + uav_export.uav_index, + ); + try wasm.globals.put(gpa, global_resolution, {}); + try wasm.global_exports.append(gpa, .{ + .name = uav_export.name, + .global_index = Wasm.GlobalIndex.fromResolution(wasm, global_resolution).?, + }); + } + } + + // Detect any intrinsics that were called; they need to have dependencies on the symbols marked. + // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized. + for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) { + .call_intrinsic => { + const symbol_name = try wasm.internString(@tagName(data.intrinsic)); + if (Wasm.FunctionIndex.fromSymbolName(wasm, symbol_name) == null and + !f.function_imports.contains(symbol_name)) + { + if (wasm.object_function_imports.getIndex(symbol_name)) |object_import_index| { + const i: Wasm.FunctionImport.Index = @fromBackingInt(@intCast(object_import_index)); + try wasm.markFunctionImport(symbol_name, i.value(wasm), i); + if (Wasm.FunctionIndex.fromSymbolName(wasm, symbol_name) == null) { + try f.function_imports.put(gpa, symbol_name, .fromObject(i, wasm)); + } + } else if (is_obj) { + const gop = try f.intrinsic_function_imports.getOrPut(gpa, symbol_name); + if (!gop.found_existing) gop.value_ptr.* = try wasm.intrinsicFunctionType(data.intrinsic); + } else { + return diags.fail("missing compiler runtime intrinsic '{t}' (undefined linker symbol)", .{ + data.intrinsic, + }); + } + } + }, + .call_indirect => { + const fn_info = zcu.typeToFunc(.fromInterned(data.ip_index)).?; + const type_index = wasm.getExistingFunctionType( + fn_info.cc, + fn_info.param_types.get(ip), + .fromInterned(fn_info.return_type), + fn_info.is_var_args, + target, + ).?; + try f.func_types.put(gpa, type_index, {}); + }, + .call_tag_index => { + assert(ip.indexToKey(data.ip_index) == .enum_type); + const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index); + if (!gop.found_existing) { + const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).backingIntType(zcu); + gop.value_ptr.* = .{ .tag_name = .{ + .symbol_name = try wasm.internStringFmt("__zig_tag_index_{d}", .{data.ip_index}), + .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .u32, false, target), + } }; + } + try wasm.functions.put(gpa, .fromZcuFunc(wasm, @fromBackingInt(@intCast(gop.index))), {}); + }, + .enum_tag_name_table_ref => { + assert(ip.indexToKey(data.ip_index) == .enum_type); + const gop = try f.enum_tag_name_table.getOrPut(gpa, data.ip_index); + if (!gop.found_existing) { + wasm.tag_name_table_ref_count += 1; + gop.value_ptr.* = @intCast(wasm.tag_name_offs.items.len); + const tag_names = ip.loadEnumType(data.ip_index).field_names; + for (tag_names.get(ip)) |tag_name| { + const slice = tag_name.toSlice(ip); + try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len)); + try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); + } + } + }, + else => continue, + }; for (f.missing_exports.keys()) |exp_name| { diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)}); @@ -300,7 +513,27 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (wasm.object_init_funcs.items.len > 0) { // Zig has no constructors so these are only for object file inputs. mem.sortUnstable(Wasm.InitFunc, wasm.object_init_funcs.items, {}, Wasm.InitFunc.lessThan); - try wasm.functions.put(gpa, .__wasm_call_ctors, {}); + if (!is_obj) try wasm.functions.put(gpa, .__wasm_call_ctors, {}); + } + + if (is_obj) { + try wasm.datas.ensureUnusedCapacity(gpa, wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len + 4); + for (0..wasm.uavs_obj.entries.len) |i| wasm.datas.putAssumeCapacity( + .pack(wasm, .{ .uav_obj = @fromBackingInt(@intCast(i)) }), + {}, + ); + for (0..wasm.navs_obj.entries.len) |i| wasm.datas.putAssumeCapacity( + .pack(wasm, .{ .nav_obj = @fromBackingInt(@intCast(i)) }), + {}, + ); + if (wasm.error_name_table_ref_count > 0) { + wasm.datas.putAssumeCapacity(.__zig_error_names, {}); + wasm.datas.putAssumeCapacity(.__zig_error_name_table, {}); + } + if (wasm.tag_name_table_ref_count > 0) { + wasm.datas.putAssumeCapacity(.__zig_tag_names, {}); + wasm.datas.putAssumeCapacity(.__zig_tag_name_table, {}); + } } // Merge and order the data segments. Depends on garbage collection so that @@ -341,14 +574,33 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // dropped in __wasm_init_memory, which is registered as the start function // We also initialize bss segments (using memory.fill) as part of this // function. - if (wasm.any_passive_inits) { + if (!is_obj and wasm.any_passive_inits) { try wasm.addFunction(.__wasm_init_memory, &.{}, &.{}); } try wasm.tables.ensureUnusedCapacity(gpa, 1); if (f.indirect_function_table.entries.len > 0) { - wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); + if (is_obj) { + const name = wasm.preloaded_strings.__indirect_function_table; + const gop = try wasm.object_table_imports.getOrPut(gpa, name); + if (!gop.found_existing) gop.value_ptr.* = .{ + .flags = .{ + .undefined = true, + .no_strip = true, + }, + .module_name = wasm.preloaded_strings.env, + .name = name, + .source_location = .zig_object_nofile, + .resolution = .unresolved, + .limits_min = 1, + .limits_max = 0, + }; + const import_index: Wasm.TableImport.Index = @fromBackingInt(@intCast(gop.index)); + try wasm.markTableImport(name, gop.value_ptr, import_index); + } else { + wasm.tables.putAssumeCapacity(.__indirect_function_table, {}); + } } // Sort order: @@ -449,7 +701,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const start_addr = alignment.forward(memory_ptr); const want_new_segment = b: { - if (is_obj) break :b false; + if (is_obj) break :b i != 0; switch (seen_tls) { .before => switch (category) { .tls => { @@ -489,7 +741,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { log.debug("0x{x} {d} {s}", .{ start_addr, @backingInt(segment_id), segment_id.name(wasm) }); memory_ptr = start_addr + size; } - if (category != .zero) try f.data_segment_groups.append(gpa, .{ + if (is_obj or category != .zero) try f.data_segment_groups.append(gpa, .{ .first_segment = first_segment, .end_addr = @intCast(memory_ptr), }); @@ -555,7 +807,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // When we have TLS GOT entries and shared memory is enabled, we must // perform runtime relocations or else we don't create the function. - if (shared_memory and virtual_addrs.tls_base != null) { + if (!is_obj and shared_memory and virtual_addrs.tls_base != null) { // This logic that checks `any_tls_relocs` is missing the part where it // also notices threadlocal globals from Zcu code. if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{}); @@ -582,6 +834,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (f.function_imports.values()) |id| { try f.func_types.put(gpa, id.functionType(wasm), {}); } + for (f.intrinsic_function_imports.values()) |type_index| { + try f.func_types.put(gpa, type_index, {}); + } for (wasm.functions.keys()) |function| { try f.func_types.put(gpa, function.typeIndex(wasm), {}); } @@ -617,7 +872,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); for (f.function_imports.values()) |id| { - const module_name = id.moduleName(wasm).slice(wasm).?; + const module_name = (id.moduleName(wasm).unwrap() orelse wasm.preloaded_strings.env).slice(wasm); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); try binary_bytes.appendSlice(gpa, module_name); @@ -631,6 +886,20 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } total_imports += f.function_imports.entries.len; + for (f.intrinsic_function_imports.keys(), f.intrinsic_function_imports.values()) |name_string, type_index| { + const module_name = wasm.preloaded_strings.env.slice(wasm); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); + try binary_bytes.appendSlice(gpa, module_name); + + const name = name_string.slice(wasm); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + + try binary_bytes.append(gpa, @backingInt(std.wasm.ExternalKind.function)); + try appendLeb128(gpa, binary_bytes, @backingInt(FuncTypeIndex.fromTypeIndex(type_index, f))); + } + total_imports += f.intrinsic_function_imports.entries.len; + for (wasm.table_imports.values()) |id| { const table_import = id.value(wasm); const module_name = table_import.module_name.slice(wasm); @@ -662,7 +931,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } for (f.global_imports.values()) |id| { - const module_name = id.moduleName(wasm).slice(wasm).?; + const module_name = (id.moduleName(wasm).unwrap() orelse wasm.preloaded_strings.env).slice(wasm); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); try binary_bytes.appendSlice(gpa, module_name); @@ -726,12 +995,12 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (wasm.globals.keys()) |global_resolution| { switch (global_resolution.unpack(wasm)) { .unresolved => unreachable, - .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base), - .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end), - .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer), - .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)), - .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?), - .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?), + .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base, is64), + .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end, is64), + .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer, is64), + .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?), is64), + .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?, is64), + .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?, is64), .object_global => |i| { const global = i.ptr(wasm); try binary_bytes.appendSlice(gpa, &.{ @@ -740,8 +1009,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { }); try emitExpr(wasm, binary_bytes, global.expr); }, - .nav_exe => unreachable, // Zig source code currently cannot represent this. - .nav_obj => unreachable, // Zig source code currently cannot represent this. + .uav_exe => |i| try appendGlobal(gpa, binary_bytes, 0, wasm.uavAddr(i.key(wasm).*), is64), + .nav_exe => |i| try appendGlobal(gpa, binary_bytes, 0, wasm.navAddr(i.key(wasm).*), is64), + .uav_obj, .nav_obj => unreachable, } } @@ -766,7 +1036,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (wasm.export_table and f.indirect_function_table.entries.len > 0) { const name = "__indirect_function_table"; - const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); + const index: u32 = @intCast(wasm.table_imports.entries.len + + wasm.tables.getIndex(.__indirect_function_table).?); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @backingInt(std.wasm.ExternalKind.table)); @@ -803,8 +1074,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // start section if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| { try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @fromBackingInt(@intCast(func_index)))); + section_index += 1; } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| { try emitStartSection(gpa, binary_bytes, func_index); + section_index += 1; } // element section @@ -812,7 +1085,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); // indirect function table elements - const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); + const table_index: u32 = @intCast( + wasm.table_imports.getIndex(wasm.preloaded_strings.__indirect_function_table) orelse + wasm.table_imports.entries.len + wasm.tables.getIndex(.__indirect_function_table).?, + ); // passive with implicit 0-index table or set table index manually const flags: u32 = if (table_index == 0) 0x0 else 0x02; try appendLeb128(gpa, binary_bytes, flags); @@ -841,11 +1117,13 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (f.data_segment_groups.items.len > 0) { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len)); + section_index += 1; } // Code section. if (wasm.functions.count() != 0) { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); + const section_offset = binary_bytes.items.len - uleb128size(@intCast(wasm.functions.count())); for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) { .unresolved => unreachable, @@ -870,10 +1148,22 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const code = ptr.code.slice(wasm); try appendLeb128(gpa, binary_bytes, code.len); const code_start = binary_bytes.items.len; + const output_offset: u32 = @intCast(binary_bytes.items.len - section_offset); try binary_bytes.appendSlice(gpa, code); - if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); + if (is_obj) { + try processRelocs( + wasm, + &f.code_relocs, + output_offset, + ptr.offset, + ptr.relocations(wasm), + ); + } else { + applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); + } }, .zcu_func => |i| { + const function_offset: u32 = @intCast(binary_bytes.items.len - section_offset); const code_start = try reserveSize(gpa, binary_bytes); defer replaceSize(binary_bytes, code_start); @@ -899,7 +1189,22 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .func_tys = undefined, .error_name_table_ref_count = undefined, }; + const body_start: u32 = @intCast(binary_bytes.items.len); + const relocs_start: u32 = @intCast(wasm.zcu_relocations.len); + defer wasm.zcu_relocations.shrinkRetainingCapacity(relocs_start); try mir.lower(wasm, binary_bytes); + const relocs_len: u32 = @intCast(wasm.zcu_relocations.len - relocs_start); + if (is_obj) { + const body_len: u32 = @intCast(binary_bytes.items.len - @as(usize, body_start)); + const output_offset = function_offset + uleb128size(body_len); + try processZcuRelocs( + wasm, + &f.code_relocs, + output_offset, + body_start, + .{ .off = relocs_start, .len = relocs_len }, + ); + } }, } }, @@ -921,8 +1226,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } } for (wasm.nav_fixups.items) |nav_fixup| { - const ds_id: Wasm.DataSegmentId = .pack(wasm, .{ .nav_exe = nav_fixup.navs_exe_index }); - const vaddr = f.data_segments.get(ds_id).? + nav_fixup.addend; + const vaddr = wasm.navAddr(nav_fixup.nav_index) + nav_fixup.addend; if (!is64) { mem.writeInt(u32, wasm.string_bytes.items[nav_fixup.offset..][0..4], vaddr, .little); } else { @@ -930,7 +1234,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } } for (wasm.func_table_fixups.items) |fixup| { - const table_index: IndirectFunctionTableIndex = .fromZcuIndirectFunctionSetIndex(fixup.table_index); + const table_index: IndirectFunctionTableIndex = .fromIpNav(wasm, fixup.nav_index); if (!is64) { mem.writeInt(u32, wasm.string_bytes.items[fixup.offset..][0..4], table_index.toAbi(), .little); } else { @@ -942,6 +1246,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { // Data section. if (f.data_segment_groups.items.len != 0) { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); + const section_offset = binary_bytes.items.len - uleb128size(@intCast(f.data_segment_groups.items.len)); var group_index: u32 = 0; var segment_offset: u32 = 0; @@ -976,7 +1281,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try appendLeb128(gpa, binary_bytes, group_size); } if (segment_id.isEmpty(wasm)) { - // It counted for virtual memory but it does not go into the binary. + if (is_obj) { + const group_size = group_end_addr - group_start_addr; + try binary_bytes.appendNTimes(gpa, 0, group_size - segment_offset); + segment_offset = group_size; + } continue; } @@ -986,6 +1295,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { segment_offset = needed_offset; const code_start = binary_bytes.items.len; + const output_offset: u32 = @intCast(binary_bytes.items.len - section_offset); append: { const code = switch (segment_id.unpack(wasm)) { .__heap_base => { @@ -1001,12 +1311,19 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { break :append; }, .__zig_error_name_table => { - if (is_obj) @panic("TODO error name table reloc"); - const base = f.data_segments.get(.__zig_error_names).?; - if (!is64) { - try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32); + if (is_obj) { + try emitRelocatableNameTable( + wasm, + binary_bytes, + &f.data_relocs, + output_offset, + wasm.error_name_offs.items, + wasm.error_name_bytes.items, + .__zig_error_names, + ); } else { - try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64); + const base = f.data_segments.get(.__zig_error_names).?; + try emitTagNameTable(wasm, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, is64); } break :append; }, @@ -1015,22 +1332,51 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { break :append; }, .__zig_tag_name_table => { - if (is_obj) @panic("TODO tag name table reloc"); - const base = f.data_segments.get(.__zig_tag_names).?; - if (!is64) { - try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32); + if (is_obj) { + try emitRelocatableNameTable( + wasm, + binary_bytes, + &f.data_relocs, + output_offset, + wasm.tag_name_offs.items, + wasm.tag_name_bytes.items, + .__zig_tag_names, + ); } else { - try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64); + const base = f.data_segments.get(.__zig_tag_names).?; + try emitTagNameTable(wasm, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, is64); } break :append; }, .object => |i| { const ptr = i.ptr(wasm); try binary_bytes.appendSlice(gpa, ptr.payload.slice(wasm)); - if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); + if (is_obj) { + try processRelocs( + wasm, + &f.data_relocs, + output_offset, + ptr.offset, + ptr.relocations(wasm), + ); + } else { + applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); + } break :append; }, - inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code, + inline .uav_obj, .nav_obj => |i| { + const zcu_data = i.value(wasm); + try binary_bytes.appendSlice(gpa, zcu_data.code.slice(wasm)); + try processZcuRelocs( + wasm, + &f.data_relocs, + output_offset, + zcu_data.code.off.unwrap().?, + zcu_data.relocs, + ); + break :append; + }, + inline .uav_exe, .nav_exe => |i| i.value(wasm).code, }; try binary_bytes.appendSlice(gpa, code.slice(wasm)); } @@ -1043,7 +1389,274 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } if (is_obj) { - @panic("TODO emit link section for object file and emit modified relocations"); + var symbol_table_offsets: SymbolTableOffsets = undefined; + { + const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); + defer writeCustomSectionHeader(binary_bytes, header_offset); + + const linking_name = "linking"; + try appendLeb128(gpa, binary_bytes, @as(u32, linking_name.len)); + try binary_bytes.appendSlice(gpa, linking_name); + + try appendLeb128(gpa, binary_bytes, @as(u32, 2)); + + // WASM_SEGMENT_INFO + { + const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); + defer replaceHeader(binary_bytes, sub_offset, @backingInt(Object.SubsectionType.segment_info)); + + const total_data_segments: u32 = @intCast(f.data_segment_groups.items.len); + try appendLeb128(gpa, binary_bytes, total_data_segments); + + for (f.data_segment_groups.items) |group| { + const segment = group.first_segment; + const name, _ = splitSegmentName(segment.name(wasm)); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + + try appendLeb128(gpa, binary_bytes, @as(u32, segment.alignment(wasm).toLog2Units())); + + var flags: u32 = 0; + if (segment.isStrings(wasm)) flags |= 1; + if (segment.isTls(wasm)) flags |= 2; + if (segment.isRetain(wasm)) flags |= 4; + try appendLeb128(gpa, binary_bytes, flags); + } + } + + // WASM_SYMBOL_TABLE + { + const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); + defer replaceHeader(binary_bytes, sub_offset, @backingInt(Object.SubsectionType.symbol_table)); + + const total_symbols: u32 = @intCast( + f.function_imports.entries.len + f.intrinsic_function_imports.entries.len + + wasm.functions.entries.len + + f.function_export_symbols.entries.len + + f.data_imports.entries.len + wasm.datas.entries.len + f.data_exports.entries.len + + f.global_imports.entries.len + wasm.globals.entries.len + + wasm.table_imports.entries.len + wasm.tables.entries.len, + ); + try appendLeb128(gpa, binary_bytes, total_symbols); + var symbol_count: u32 = 0; + + // SYMTAB_FUNCTION + { + symbol_table_offsets.function = symbol_count; + for (f.function_imports.values(), 0..) |i, function_index| { + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.function)); + const flags = i.flags(wasm); + assert(flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + if (flags.explicit_name) { + unreachable; // never set + } + symbol_count += 1; + } + const intrinsic_flags: Wasm.SymbolFlags = .{ .undefined = true }; + for (f.intrinsic_function_imports.keys(), f.function_imports.entries.len..) |_, function_index| { + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.function)); + try appendLeb128(gpa, binary_bytes, intrinsic_flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + symbol_count += 1; + } + for ( + wasm.functions.keys(), + f.function_imports.entries.len + f.intrinsic_function_imports.entries.len.., + ) |resolution, function_index| { + const name = resolution.name(wasm).?; + const flags = resolution.flags(wasm); + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.function)); + assert(!flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + symbol_count += 1; + } + for ( + f.function_export_symbols.keys(), + f.function_export_symbols.values(), + ) |name_string, symbol| { + const name = name_string.slice(wasm); + const function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex( + wasm, + symbol.function_index, + ); + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.function)); + try appendLeb128(gpa, binary_bytes, symbol.flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @backingInt(function_index)); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + symbol_count += 1; + } + } + + // SYMTAB_DATA + { + symbol_table_offsets.data = symbol_count; + for (f.data_imports.keys(), f.data_imports.values()) |name_string, data_index| { + const name = name_string.slice(wasm); + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.data)); + const flags = data_index.flags(wasm); + assert(flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + symbol_count += 1; + } + for (wasm.datas.keys()) |resolution| { + var buf: [32]u8 = undefined; + const name = resolution.name(wasm, &buf); + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.data)); + const flags = resolution.flags(wasm); + assert(!flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + + const data_loc = resolution.dataLoc(wasm); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + + const segment_index = f.data_segments.getIndex(data_loc.segment).?; + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(segment_index))); + try appendLeb128(gpa, binary_bytes, data_loc.offset); + try appendLeb128(gpa, binary_bytes, resolution.size(wasm)); + symbol_count += 1; + } + for (f.data_exports.keys(), f.data_exports.values()) |name_string, symbol| { + const name = name_string.slice(wasm); + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.data)); + try appendLeb128(gpa, binary_bytes, symbol.flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + + const data_loc = symbol.resolution.dataLoc(wasm); + const segment_index = f.data_segments.getIndex(data_loc.segment).?; + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(segment_index))); + try appendLeb128(gpa, binary_bytes, data_loc.offset); + try appendLeb128(gpa, binary_bytes, symbol.resolution.size(wasm)); + symbol_count += 1; + } + } + + // SYMTAB_GLOBAL + { + symbol_table_offsets.global = symbol_count; + for (f.global_imports.values(), 0..) |i, global_index| { + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.global)); + const flags = i.flags(wasm); + assert(flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(global_index))); + if (flags.explicit_name) { + unreachable; // never set + } + symbol_count += 1; + } + for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { + var buf: [32]u8 = undefined; + const name = resolution.name(wasm, &buf).?; + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.global)); + const flags = resolution.flags(wasm); + assert(!flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(global_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + symbol_count += 1; + } + } + + // SYMTAB_EVENT + { + // TODO not parsed yet + } + + // SYMTAB_SECTION + { + // TODO not parsed correctly yet + } + + // SYMTAB_TABLE + { + symbol_table_offsets.table = symbol_count; + for (wasm.table_imports.values(), 0..) |i, table_index| { + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.table)); + const flags = i.value(wasm).flags; + assert(flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(table_index))); + if (flags.explicit_name) { + unreachable; // never set + } + symbol_count += 1; + } + for (wasm.tables.keys(), wasm.table_imports.entries.len..) |resolution, table_index| { + const name = resolution.name(wasm).?; + try binary_bytes.append(gpa, @backingInt(Object.Symbol.Tag.table)); + const flags = resolution.flags(wasm); + assert(!flags.undefined); + try appendLeb128(gpa, binary_bytes, flags.toAbiInteger()); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(table_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + symbol_count += 1; + } + } + assert(symbol_count == total_symbols); + } + + // WASM_INIT_FUNCS + { + const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); + defer replaceHeader(binary_bytes, sub_offset, @backingInt(Object.SubsectionType.init_funcs)); + + const init_funcs = wasm.object_init_funcs.items; + const total_functions: u32 = b: { + var cnt: u32 = 0; + for (init_funcs) |init_func| { + const func = init_func.function_index.ptr(wasm); + if (!func.object_index.ptr(wasm).is_included) continue; + cnt += 1; + } + break :b cnt; + }; + try appendLeb128(gpa, binary_bytes, total_functions); + + for (init_funcs) |init_func| { + const func = init_func.function_index.ptr(wasm); + if (!func.object_index.ptr(wasm).is_included) continue; + + try appendLeb128(gpa, binary_bytes, init_func.priority); + const out_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index); + const symbol_index: u32 = symbol_table_offsets.function + @backingInt(out_index); + try appendLeb128(gpa, binary_bytes, symbol_index); + } + } + + // WASM_COMDAT_INFO + { + // TODO + } + } + + if (f.code_relocs.items.len != 0) try emitRelocSection( + wasm, + binary_bytes, + code_section_index.?, + "reloc.CODE", + f.code_relocs.items, + symbol_table_offsets, + ); + if (f.data_relocs.items.len != 0) try emitRelocSection( + wasm, + binary_bytes, + data_section_index.?, + "reloc.DATA", + f.data_relocs.items, + symbol_table_offsets, + ); } else if (comp.config.debug_format != .strip) { try emitNameSection(wasm, f.data_segment_groups.items, binary_bytes); } @@ -1121,7 +1734,10 @@ fn emitNameSection( const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer replaceHeader(binary_bytes, sub_offset, @backingInt(std.wasm.NameSubsection.function)); - const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len); + const total_functions: u32 = @intCast( + f.function_imports.entries.len + f.intrinsic_function_imports.entries.len + + wasm.functions.entries.len, + ); try appendLeb128(gpa, binary_bytes, total_functions); for (f.function_imports.keys(), 0..) |name_index, function_index| { @@ -1130,7 +1746,16 @@ fn emitNameSection( try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } - for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { + for (f.intrinsic_function_imports.keys(), f.function_imports.entries.len..) |name_index, function_index| { + const name = name_index.slice(wasm); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); + } + for ( + wasm.functions.keys(), + f.function_imports.entries.len + f.intrinsic_function_imports.entries.len.., + ) |resolution, function_index| { const name = resolution.name(wasm).?; try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); @@ -1152,7 +1777,8 @@ fn emitNameSection( try binary_bytes.appendSlice(gpa, name); } for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { - const name = resolution.name(wasm).?; + var buf: [32]u8 = undefined; + const name = resolution.name(wasm, &buf).?; try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(global_index))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); @@ -1418,29 +2044,6 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *ArrayList(u8), expr: Wasm.Expr try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode } -fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.array_list.Managed(u8)) !void { - const gpa = wasm.base.comp.gpa; - try appendLeb128(gpa, binary_bytes, @backingInt(Wasm.SubsectionType.segment_info)); - const segment_offset = binary_bytes.items.len; - - try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(wasm.segment_info.count()))); - for (wasm.segment_info.values()) |segment_info| { - log.debug("Emit segment: {s} align({d}) flags({b})", .{ - segment_info.name, - segment_info.alignment, - segment_info.flags, - }); - try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(segment_info.name.len))); - try binary_bytes.appendSlice(gpa, segment_info.name); - try appendLeb128(gpa, binary_bytes, segment_info.alignment.toLog2Units()); - try appendLeb128(gpa, binary_bytes, segment_info.flags); - } - - var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset))); - try binary_bytes.insertSlice(segment_offset, &buf); -} - fn uleb128size(x: u32) u32 { var value = x; var size: u32 = 0; @@ -1449,22 +2052,395 @@ fn uleb128size(x: u32) u32 { } fn emitTagNameTable( - gpa: Allocator, + wasm: *const Wasm, code: *ArrayList(u8), tag_name_offs: []const u32, tag_name_bytes: []const u8, base: u32, - comptime Int: type, + is64: bool, ) error{OutOfMemory}!void { - const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8); + const gpa = wasm.base.comp.gpa; + const ptr_size_bytes: usize = if (is64) 8 else 4; try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len); for (tag_name_offs) |off| { const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?); - mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little); - mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little); + if (is64) { + mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), base + off, .little); + mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), name_len, .little); + } else { + mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), base + off, .little); + mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), name_len, .little); + } } } +fn emitRelocatableNameTable( + wasm: *const Wasm, + code: *ArrayList(u8), + relocs: *ArrayList(Relocation), + output_offset: u32, + name_offs: []const u32, + name_bytes: []const u8, + names_resolution: Wasm.ObjectDataImport.Resolution, +) error{OutOfMemory}!void { + const gpa = wasm.base.comp.gpa; + const ptr_size = @divExact(wasm.base.comp.root_mod.resolved_target.result.ptrBitWidth(), 8); + const table_start = code.items.len; + const data_index: DataSymbolIndex = .fromResolution(wasm, names_resolution); + try code.ensureUnusedCapacity(gpa, @as(usize, ptr_size) * 2 * name_offs.len); + try relocs.ensureUnusedCapacity(gpa, name_offs.len); + for (name_offs) |off| { + const name_len: u32 = @intCast(mem.indexOfScalar(u8, name_bytes[off..], 0).?); + const reloc_offset = output_offset + @as(u32, @intCast(code.items.len - table_start)); + switch (ptr_size) { + 4 => { + @memset(code.addManyAsArrayAssumeCapacity(4), 0); + mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), name_len, .little); + }, + 8 => { + @memset(code.addManyAsArrayAssumeCapacity(8), 0); + mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), @intCast(name_len), .little); + }, + else => unreachable, + } + relocs.appendAssumeCapacity(.{ + .tag = if (ptr_size == 4) .memory_addr_i32 else .memory_addr_i64, + .offset = reloc_offset, + .pointee = .{ .data = data_index }, + .addend = @intCast(off), + }); + } +} + +fn emitRelocSection( + wasm: *const Wasm, + binary_bytes: *ArrayList(u8), + section_index: u32, + reloc_name: []const u8, + relocs: []const Relocation, + symbol_table_offsets: SymbolTableOffsets, +) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + + const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); + defer writeCustomSectionHeader(binary_bytes, header_offset); + + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(reloc_name.len))); + try binary_bytes.appendSlice(gpa, reloc_name); + + try appendLeb128(gpa, binary_bytes, section_index); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(relocs.len))); + + for (relocs) |r| { + try binary_bytes.append(gpa, @backingInt(r.tag)); + try appendLeb128(gpa, binary_bytes, r.offset); + switch (r.tag) { + .memory_addr_leb, + .memory_addr_sleb, + .memory_addr_i32, + .memory_addr_rel_sleb, + .memory_addr_leb64, + .memory_addr_sleb64, + .memory_addr_i64, + .memory_addr_rel_sleb64, + .memory_addr_tls_sleb, + .memory_addr_locrel_i32, + .memory_addr_tls_sleb64, + => { + const symbol_index: u32 = symbol_table_offsets.data + @backingInt(r.pointee.data); + try appendLeb128(gpa, binary_bytes, symbol_index); + }, + .section_offset_i32 => { + @panic("TODO"); + }, + .type_index_leb => { + try appendLeb128(gpa, binary_bytes, @backingInt(r.pointee.type_index)); + }, + .function_offset_i32, + .function_offset_i64, + .function_index_leb, + .function_index_i32, + .table_index_sleb, + .table_index_i32, + .table_index_sleb64, + .table_index_i64, + .table_index_rel_sleb, + .table_index_rel_sleb64, + => { + const symbol_index: u32 = symbol_table_offsets.function + @backingInt(r.pointee.function); + try appendLeb128(gpa, binary_bytes, symbol_index); + }, + .global_index_leb, .global_index_i32 => { + const symbol_index: u32 = symbol_table_offsets.global + @backingInt(r.pointee.global); + try appendLeb128(gpa, binary_bytes, symbol_index); + }, + .table_number_leb => { + const symbol_index: u32 = symbol_table_offsets.table + @backingInt(r.pointee.table); + try appendLeb128(gpa, binary_bytes, symbol_index); + }, + .event_index_leb => @panic("TODO"), + } + switch (r.tag) { + .memory_addr_leb, + .memory_addr_sleb, + .memory_addr_i32, + .memory_addr_rel_sleb, + .memory_addr_leb64, + .memory_addr_sleb64, + .memory_addr_i64, + .memory_addr_rel_sleb64, + .memory_addr_tls_sleb, + .memory_addr_locrel_i32, + .memory_addr_tls_sleb64, + .function_offset_i32, + .function_offset_i64, + .section_offset_i32, + => { + try appendLeb128(gpa, binary_bytes, r.addend); + }, + else => {}, + } + } +} + +fn processZcuRelocs( + wasm: *const Wasm, + out: *ArrayList(Relocation), + output_offset: u32, + input_offset: u32, + relocs: Wasm.ZcuRelocation.Slice, +) !void { + const gpa = wasm.base.comp.gpa; + for ( + relocs.tags(wasm), + relocs.pointees(wasm), + relocs.offsets(wasm), + relocs.addends(wasm), + ) |tag, pointee, offset, addend| { + const output_pointee: Relocation.Pointee = switch (pointee) { + .function_nav => |nav_index| .{ .function = .fromIpNav(wasm, nav_index) }, + .function_name => |name| .{ .function = .fromSymbolName(wasm, name) }, + .tag_function => |ip_index| .{ .function = .fromTagIndexType(wasm, ip_index) }, + .data_uav => |ip_index| .{ .data = .fromUav(wasm, ip_index) }, + .data_nav => |nav_index| .{ .data = .fromNav(wasm, nav_index) }, + .data_resolution => |resolution| .{ .data = .fromResolution(wasm, resolution) }, + .stack_pointer => .{ .global = .fromSymbolName(wasm, wasm.preloaded_strings.__stack_pointer) }, + .type_index => |type_index| .{ .type_index = .fromTypeIndex(type_index, &wasm.flush_buffer) }, + }; + try out.append(gpa, .{ + .tag = tag, + .offset = output_offset + (offset - input_offset), + .pointee = output_pointee, + .addend = addend, + }); + } +} + +fn processRelocs( + wasm: *const Wasm, + out: *ArrayList(Relocation), + output_offset: u32, + input_offset: u32, + relocs: Wasm.ObjectRelocation.IterableSlice, +) !void { + const gpa = wasm.base.comp.gpa; + for ( + relocs.slice.tags(wasm), + relocs.slice.pointees(wasm), + relocs.slice.offsets(wasm), + relocs.slice.addends(wasm), + ) |tag, pointee, offset, addend| { + if (offset >= relocs.end) break; + const rebased_offset = output_offset + (offset - input_offset); + try out.ensureUnusedCapacity(gpa, 1); + switch (tag) { + .function_index_i32 => out.appendAssumeCapacity(.{ + .tag = .function_index_i32, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + .function_index_leb => out.appendAssumeCapacity(.{ + .tag = .function_index_leb, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + .function_offset_i32 => @panic("TODO this value is not known yet"), + .function_offset_i64 => @panic("TODO this value is not known yet"), + .table_index_i32 => out.appendAssumeCapacity(.{ + .tag = .table_index_i32, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + .table_index_i64 => out.appendAssumeCapacity(.{ + .tag = .table_index_i64, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + .table_index_rel_sleb => @panic("TODO what does this reloc tag mean?"), + .table_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"), + .table_index_sleb => out.appendAssumeCapacity(.{ + .tag = .table_index_sleb, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + .table_index_sleb64 => out.appendAssumeCapacity(.{ + .tag = .table_index_sleb64, + .offset = rebased_offset, + .pointee = .{ .function = .fromObjectFunctionHandlingWeak(wasm, pointee.function) }, + .addend = addend, + }), + + .function_import_index_i32 => out.appendAssumeCapacity(.{ + .tag = .function_index_i32, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .function_import_index_leb => out.appendAssumeCapacity(.{ + .tag = .function_index_leb, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .function_import_offset_i32 => @panic("TODO this value is not known yet"), + .function_import_offset_i64 => @panic("TODO this value is not known yet"), + .table_import_index_i32 => out.appendAssumeCapacity(.{ + .tag = .table_index_i32, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .table_import_index_i64 => out.appendAssumeCapacity(.{ + .tag = .table_index_i64, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .table_import_index_rel_sleb => @panic("TODO what does this reloc tag mean?"), + .table_import_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"), + .table_import_index_sleb => out.appendAssumeCapacity(.{ + .tag = .table_index_sleb, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .table_import_index_sleb64 => out.appendAssumeCapacity(.{ + .tag = .table_index_sleb64, + .offset = rebased_offset, + .pointee = .{ .function = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + + .global_index_i32 => out.appendAssumeCapacity(.{ + .tag = .global_index_i32, + .offset = rebased_offset, + .pointee = .{ .global = .fromObjectGlobalHandlingWeak(wasm, pointee.global) }, + .addend = addend, + }), + .global_index_leb => out.appendAssumeCapacity(.{ + .tag = .global_index_leb, + .offset = rebased_offset, + .pointee = .{ .global = .fromObjectGlobalHandlingWeak(wasm, pointee.global) }, + .addend = addend, + }), + + .global_import_index_i32 => out.appendAssumeCapacity(.{ + .tag = .global_index_i32, + .offset = rebased_offset, + .pointee = .{ .global = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .global_import_index_leb => out.appendAssumeCapacity(.{ + .tag = .global_index_leb, + .offset = rebased_offset, + .pointee = .{ .global = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + + .memory_addr_i32, + .memory_addr_i64, + .memory_addr_leb, + .memory_addr_leb64, + .memory_addr_sleb, + .memory_addr_sleb64, + .memory_addr_tls_sleb, + .memory_addr_tls_sleb64, + => out.appendAssumeCapacity(.{ + .tag = memoryRelocationType(tag), + .offset = rebased_offset, + .pointee = .{ .data = .fromObjectData(wasm, pointee.data) }, + .addend = addend, + }), + .memory_addr_locrel_i32 => @panic("TODO implement relocation memory_addr_locrel_i32"), + .memory_addr_rel_sleb => @panic("TODO implement relocation memory_addr_rel_sleb"), + .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"), + + .memory_addr_import_i32, + .memory_addr_import_i64, + .memory_addr_import_leb, + .memory_addr_import_leb64, + .memory_addr_import_sleb, + .memory_addr_import_sleb64, + => out.appendAssumeCapacity(.{ + .tag = memoryRelocationType(tag), + .offset = rebased_offset, + .pointee = .{ .data = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + .memory_addr_import_locrel_i32 => @panic("TODO implement relocation memory_addr_import_locrel_i32"), + .memory_addr_import_rel_sleb => @panic("TODO implement relocation memory_addr_import_rel_sleb"), + .memory_addr_import_rel_sleb64 => @panic("TODO implement memory_addr_import_rel_sleb64"), + .memory_addr_import_tls_sleb => @panic("TODO"), + .memory_addr_import_tls_sleb64 => @panic("TODO"), + + .section_offset_i32 => @panic("TODO this value is not known yet"), + + .table_number_leb => out.appendAssumeCapacity(.{ + .tag = .table_number_leb, + .offset = rebased_offset, + .pointee = .{ .table = .fromObjectTable(wasm, pointee.table) }, + .addend = addend, + }), + .table_import_number_leb => out.appendAssumeCapacity(.{ + .tag = .table_number_leb, + .offset = rebased_offset, + .pointee = .{ .table = .fromSymbolName(wasm, pointee.symbol_name) }, + .addend = addend, + }), + + .type_index_leb => out.appendAssumeCapacity(.{ + .tag = .type_index_leb, + .offset = rebased_offset, + .pointee = .{ .type_index = .fromTypeIndex(pointee.type_index, &wasm.flush_buffer) }, + .addend = addend, + }), + } + } +} + +fn memoryRelocationType(tag: Wasm.ObjectRelocation.Tag) Object.RelocationType { + return switch (tag) { + .memory_addr_i32, .memory_addr_import_i32 => .memory_addr_i32, + .memory_addr_i64, .memory_addr_import_i64 => .memory_addr_i64, + .memory_addr_leb, .memory_addr_import_leb => .memory_addr_leb, + .memory_addr_leb64, .memory_addr_import_leb64 => .memory_addr_leb64, + .memory_addr_locrel_i32, .memory_addr_import_locrel_i32 => .memory_addr_locrel_i32, + .memory_addr_rel_sleb, .memory_addr_import_rel_sleb => .memory_addr_rel_sleb, + .memory_addr_rel_sleb64, .memory_addr_import_rel_sleb64 => .memory_addr_rel_sleb64, + .memory_addr_sleb, .memory_addr_import_sleb => .memory_addr_sleb, + .memory_addr_sleb64, .memory_addr_import_sleb64 => .memory_addr_sleb64, + .memory_addr_tls_sleb, .memory_addr_import_tls_sleb => .memory_addr_tls_sleb, + .memory_addr_tls_sleb64, .memory_addr_import_tls_sleb64 => .memory_addr_tls_sleb64, + else => unreachable, + }; +} + fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.IterableSlice, wasm: *const Wasm) void { for ( relocs.slice.tags(wasm), @@ -1579,12 +2555,17 @@ const RelocAddr = struct { fn fromSymbolName(wasm: *const Wasm, name: String, addend: i32) RelocAddr { const flush = &wasm.flush_buffer; if (wasm.object_data_imports.getPtr(name)) |import| { - return fromDataLoc(flush, import.resolution.dataLoc(wasm), addend); - } else if (wasm.data_imports.get(name)) |id| { + if (import.resolution != .unresolved) { + return fromDataLoc(flush, import.resolution.dataLoc(wasm), addend); + } + } + if (flush.data_exports.get(name)) |symbol| { + return fromDataLoc(flush, symbol.resolution.dataLoc(wasm), addend); + } + if (wasm.data_imports.get(name)) |id| { return fromDataLoc(flush, .fromDataImportId(wasm, id), addend); - } else { - unreachable; } + unreachable; } fn fromDataLoc(flush: *const Flush, data_loc: Wasm.DataLoc, addend: i32) RelocAddr { @@ -1702,13 +2683,11 @@ fn emitInitMemoryFunction( } const segment_groups = wasm.flush_buffer.data_segment_groups.items; - var prev_end: u32 = 0; for (segment_groups, 0..) |group, segment_index| { - defer prev_end = group.end_addr; const segment = group.first_segment; if (!segment.isPassive(wasm)) continue; - const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end)); + const start_addr = wasm.flush_buffer.data_segments.get(segment).?; const segment_size: u32 = group.end_addr - start_addr; try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 5 + 6 + 6 + 1 + 6 * 2 + 1 + 1); @@ -2028,11 +3007,15 @@ fn appendReservedUleb32(bytes: *ArrayList(u8), val: u32) void { }; } -fn appendGlobal(gpa: Allocator, bytes: *ArrayList(u8), mutable: u8, val: u32) Allocator.Error!void { - try bytes.ensureUnusedCapacity(gpa, 9); - bytes.appendAssumeCapacity(@backingInt(std.wasm.Valtype.i32)); +fn appendGlobal(gpa: Allocator, bytes: *ArrayList(u8), mutable: u8, val: u64, is64: bool) Allocator.Error!void { + try bytes.ensureUnusedCapacity(gpa, if (is64) 14 else 9); + bytes.appendAssumeCapacity(@backingInt(@as(std.wasm.Valtype, if (is64) .i64 else .i32))); bytes.appendAssumeCapacity(mutable); - appendReservedI32Const(bytes, val); + if (is64) { + appendReservedI64Const(bytes, val); + } else { + appendReservedI32Const(bytes, @intCast(val)); + } bytes.appendAssumeCapacity(@backingInt(std.wasm.Opcode.end)); } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index a9b5a1fe42a27b160b833b9854468ebbba419c17..44359b315bdec094029d9ceed782f798ea32f25f 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -146,7 +146,7 @@ pub const Symbol = struct { pointee: Pointee, /// https://github.com/WebAssembly/tool-conventions/blob/df8d737539eb8a8f446ba5eab9dc670c40dfb81e/Linking.md#symbol-table-subsection - const Tag = enum(u8) { + pub const Tag = enum(u8) { function, data, global, diff --git a/src/target.zig b/src/target.zig index b4dc16b3185bb4e60ec99d93e2a39595f815eeed..82728f3942f07da8fe6d64c922c69a91e8e7ae66 100644 --- a/src/target.zig +++ b/src/target.zig @@ -437,7 +437,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_onl else => {}, } return switch (zigBackend(target, false)) { - .stage2_aarch64, .stage2_x86_64 => .yes, + .stage2_aarch64, .stage2_wasm, .stage2_x86_64 => .yes, else => .llvm_only, }; } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index d1acfb366dbfe0620409a81ef565b0902237a81b..42c85b0487e2d024adf394300c165feaa126d325 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -797,7 +797,6 @@ test "auto created variables have correct alignment" { } test "extern variable with non-pointer opaque type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO diff --git a/test/behavior/builtin_functions_returning_void_or_noreturn.zig b/test/behavior/builtin_functions_returning_void_or_noreturn.zig index cd3bc58de61600a0ad57d745355b5b707b4e5322..820a920464c77e42b6130b5d96838d7eda7a3d9a 100644 --- a/test/behavior/builtin_functions_returning_void_or_noreturn.zig +++ b/test/behavior/builtin_functions_returning_void_or_noreturn.zig @@ -6,7 +6,6 @@ var x: u8 = 1; // This excludes builtin functions that return void or noreturn that cannot be tested. test { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 94e19e723d60cb8cdf2f5cfc1aaa7306ec7bbd81..5f2b24b6b72eadd8dfffa6998ee996e292ed381c 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -21,7 +21,6 @@ test "super basic invocations" { test "basic invocations" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/export_builtin.zig b/test/behavior/export_builtin.zig index 16fc0a7a79c20795b81abf4282c00cf7e1c54258..0381e9645488d4565a56c16630c9e937e152df61 100644 --- a/test/behavior/export_builtin.zig +++ b/test/behavior/export_builtin.zig @@ -5,11 +5,6 @@ const expect = std.testing.expect; test "exporting enum value" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) { - // https://github.com/ziglang/zig/issues/4866 - return error.SkipZigTest; - } - const S = struct { const E = enum(c_int) { one, two }; const e: E = .two; @@ -35,11 +30,6 @@ test "exporting with internal linkage" { test "exporting using namespace access" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) { - // https://github.com/ziglang/zig/issues/4866 - return error.SkipZigTest; - } - const S = struct { const Inner = struct { const x: u32 = 5; @@ -57,11 +47,6 @@ test "exporting comptime-known value" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.cpu.arch.isWasm()) { - // https://github.com/ziglang/zig/issues/4866 - return error.SkipZigTest; - } - const x: u32 = 10; @export(&x, .{ .name = "exporting_comptime_known_value_foo" }); const S = struct { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 47e6be86bb715f90bae1eb08a9237af2f07c1515..69b32ca20271f8bbdf660eb5cc8039c6e0a5bfb1 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -418,7 +418,6 @@ test "import passed byref to function in return type" { test "implicit cast function to function ptr" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S1 = struct { diff --git a/test/tests.zig b/test/tests.zig index d823d2add16c8a4486decb3671025cd60cbc16e2..390154d8fc4cd3f1a9b30fdba87da448d6cbd0e0 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1568,7 +1568,6 @@ const module_test_targets = blk: { .os_tag = .wasi, .abi = .none, }, - .skip_modules = &.{"compiler-rt"}, .use_llvm = false, .use_lld = false, },