authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-17 21:12:01-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
log458f658b427c26ede2776a04113ba00a3a491793
tree0dcb397bb124a786d35697a93265305f800da7f1
parent761387dc556e065e39d021bf96fb4d4c8873a145

wasm linker: implement missing logic

fix some compilation errors for reworked Emit now that it's actually referenced introduce DataSegment.Id for sorting data both from object files and from the Zcu. introduce optimization: data segment sorting includes a descending sort on reference count so that references to data can be smaller integers leading to better LEB encodings. this optimization is skipped for object files. implement uav address access function which is based on only 1 hash table lookup to find out the offset after sorting.

8 files changed, 622 insertions(+), 253 deletions(-)

src/InternPool.zig+7
...@@ -620,6 +620,13 @@ pub const Nav = struct {...@@ -620,6 +620,13 @@ pub const Nav = struct {
620 };620 };
621 }621 }
622622
623 /// Asserts that `status == .resolved`.
624 pub fn isThreadLocal(nav: Nav, ip: *const InternPool) bool {
625 const val = nav.status.resolved.val;
626 if (!isVariable(ip, val)) return false;
627 return ip.indexToKey(val).variable.is_threadlocal;
628 }
629
623 /// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.630 /// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
624 /// This is a `declaration`.631 /// This is a `declaration`.
625 pub fn srcInst(nav: Nav, ip: *const InternPool) TrackedInst.Index {632 pub fn srcInst(nav: Nav, ip: *const InternPool) TrackedInst.Index {
src/arch/wasm/CodeGen.zig+27-10
...@@ -944,8 +944,11 @@ fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {...@@ -944,8 +944,11 @@ fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
944 cg.mir_extra.appendAssumeCapacity(switch (field.type) {944 cg.mir_extra.appendAssumeCapacity(switch (field.type) {
945 u32 => @field(extra, field.name),945 u32 => @field(extra, field.name),
946 i32 => @bitCast(@field(extra, field.name)),946 i32 => @bitCast(@field(extra, field.name)),
947 InternPool.Index => @intFromEnum(@field(extra, field.name)),947 InternPool.Index,
948 InternPool.Nav.Index => @intFromEnum(@field(extra, field.name)),948 InternPool.Nav.Index,
949 Wasm.UavsObjIndex,
950 Wasm.UavsExeIndex,
951 => @intFromEnum(@field(extra, field.name)),
949 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),952 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
950 });953 });
951 }954 }
...@@ -1034,14 +1037,26 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -1034,14 +1037,26 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1034 }1037 }
1035 },1038 },
1036 .uav_ref => |uav| {1039 .uav_ref => |uav| {
1040 const wasm = cg.wasm;
1041 const is_obj = wasm.base.comp.config.output_mode == .Obj;
1037 if (uav.offset == 0) {1042 if (uav.offset == 0) {
1038 try cg.addInst(.{ .tag = .uav_ref, .data = .{ .ip_index = uav.ip_index } });1043 try cg.addInst(.{
1044 .tag = .uav_ref,
1045 .data = if (is_obj) .{
1046 .uav_obj = try wasm.refUavObj(cg.pt, uav.ip_index),
1047 } else .{
1048 .uav_exe = try wasm.refUavExe(cg.pt, uav.ip_index),
1049 },
1050 });
1039 } else {1051 } else {
1040 try cg.addInst(.{1052 try cg.addInst(.{
1041 .tag = .uav_ref_off,1053 .tag = .uav_ref_off,
1042 .data = .{1054 .data = .{
1043 .payload = try cg.addExtra(Mir.UavRefOff{1055 .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{
1044 .ip_index = uav.ip_index,1056 .uav_obj = try wasm.refUavObj(cg.pt, uav.ip_index),
1057 .offset = uav.offset,
1058 }) else try cg.addExtra(Mir.UavRefOffExe{
1059 .uav_exe = try wasm.refUavExe(cg.pt, uav.ip_index),
1045 .offset = uav.offset,1060 .offset = uav.offset,
1046 }),1061 }),
1047 },1062 },
...@@ -1148,11 +1163,11 @@ pub const Function = extern struct {...@@ -1148,11 +1163,11 @@ pub const Function = extern struct {
1148 }1163 }
1149 };1164 };
11501165
1151 pub fn lower(f: *Function, wasm: *const Wasm, code: *std.ArrayList(u8)) Allocator.Error!void {1166 pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
1152 const gpa = wasm.base.comp.gpa;1167 const gpa = wasm.base.comp.gpa;
11531168
1154 // Write the locals in the prologue of the function body.1169 // Write the locals in the prologue of the function body.
1155 const locals = wasm.all_zcu_locals[f.locals_off..][0..f.locals_len];1170 const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len];
1156 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);1171 try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38);
11571172
1158 std.leb.writeUleb128(code.writer(gpa), @as(u32, @intCast(locals.len))) catch unreachable;1173 std.leb.writeUleb128(code.writer(gpa), @as(u32, @intCast(locals.len))) catch unreachable;
...@@ -1164,7 +1179,7 @@ pub const Function = extern struct {...@@ -1164,7 +1179,7 @@ pub const Function = extern struct {
1164 // Stack management section of function prologue.1179 // Stack management section of function prologue.
1165 const stack_alignment = f.prologue.flags.stack_alignment;1180 const stack_alignment = f.prologue.flags.stack_alignment;
1166 if (stack_alignment.toByteUnits()) |align_bytes| {1181 if (stack_alignment.toByteUnits()) |align_bytes| {
1167 const sp_global = try wasm.stackPointerGlobalIndex();1182 const sp_global: Wasm.GlobalIndex = .stack_pointer;
1168 // load stack pointer1183 // load stack pointer
1169 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));1184 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));
1170 std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable;1185 std.leb.writeULEB128(code.writer(gpa), @intFromEnum(sp_global)) catch unreachable;
...@@ -1172,7 +1187,7 @@ pub const Function = extern struct {...@@ -1172,7 +1187,7 @@ pub const Function = extern struct {
1172 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));1187 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));
1173 leb.writeUleb128(code.writer(gpa), f.prologue.sp_local) catch unreachable;1188 leb.writeUleb128(code.writer(gpa), f.prologue.sp_local) catch unreachable;
1174 // get the total stack size1189 // get the total stack size
1175 const aligned_stack: i32 = @intCast(f.stack_alignment.forward(f.prologue.stack_size));1190 const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size));
1176 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1177 leb.writeIleb128(code.writer(gpa), aligned_stack) catch unreachable;1192 leb.writeIleb128(code.writer(gpa), aligned_stack) catch unreachable;
1178 // subtract it from the current stack pointer1193 // subtract it from the current stack pointer
...@@ -1197,7 +1212,7 @@ pub const Function = extern struct {...@@ -1197,7 +1212,7 @@ pub const Function = extern struct {
1197 .mir = .{1212 .mir = .{
1198 .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len],1213 .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len],
1199 .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len],1214 .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len],
1200 .extra = wasm.mir_extra[f.mir_extra_off..][0..f.mir_extra_len],1215 .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len],
1201 },1216 },
1202 .wasm = wasm,1217 .wasm = wasm,
1203 .code = code,1218 .code = code,
...@@ -5846,6 +5861,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5846,6 +5861,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5846 const name_ty = Type.slice_const_u8_sentinel_0;5861 const name_ty = Type.slice_const_u8_sentinel_0;
5847 const abi_size = name_ty.abiSize(pt.zcu);5862 const abi_size = name_ty.abiSize(pt.zcu);
58485863
5864 cg.wasm.error_name_table_ref_count += 1;
5865
5849 // Lowers to a i32.const or i64.const with the error table memory address.5866 // Lowers to a i32.const or i64.const with the error table memory address.
5850 try cg.addTag(.error_name_table_ref);5867 try cg.addTag(.error_name_table_ref);
5851 try cg.emitWValue(operand);5868 try cg.emitWValue(operand);
src/arch/wasm/Emit.zig+43-52
...@@ -5,6 +5,7 @@ const assert = std.debug.assert;...@@ -5,6 +5,7 @@ const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const leb = std.leb;6const leb = std.leb;
77
8const Wasm = link.File.Wasm;
8const Mir = @import("Mir.zig");9const Mir = @import("Mir.zig");
9const link = @import("../../link.zig");10const link = @import("../../link.zig");
10const Zcu = @import("../../Zcu.zig");11const Zcu = @import("../../Zcu.zig");
...@@ -12,7 +13,7 @@ const InternPool = @import("../../InternPool.zig");...@@ -12,7 +13,7 @@ const InternPool = @import("../../InternPool.zig");
12const codegen = @import("../../codegen.zig");13const codegen = @import("../../codegen.zig");
1314
14mir: Mir,15mir: Mir,
15wasm: *link.File.Wasm,16wasm: *Wasm,
16/// The binary representation that will be emitted by this module.17/// The binary representation that will be emitted by this module.
17code: *std.ArrayListUnmanaged(u8),18code: *std.ArrayListUnmanaged(u8),
1819
...@@ -30,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -30,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {
30 const target = &comp.root_mod.resolved_target.result;31 const target = &comp.root_mod.resolved_target.result;
31 const is_wasm32 = target.cpu.arch == .wasm32;32 const is_wasm32 = target.cpu.arch == .wasm32;
3233
33 const tags = mir.instructions.items(.tag);34 const tags = mir.instruction_tags;
34 const datas = mir.instructions.items(.data);35 const datas = mir.instruction_datas;
35 var inst: u32 = 0;36 var inst: u32 = 0;
3637
37 loop: switch (tags[inst]) {38 loop: switch (tags[inst]) {
...@@ -48,17 +49,25 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -48,17 +49,25 @@ pub fn lowerToCode(emit: *Emit) Error!void {
48 continue :loop tags[inst];49 continue :loop tags[inst];
49 },50 },
50 .uav_ref => {51 .uav_ref => {
51 try uavRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 });52 if (is_obj) {
53 try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32);
54 } else {
55 try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32);
56 }
52 inst += 1;57 inst += 1;
53 continue :loop tags[inst];58 continue :loop tags[inst];
54 },59 },
55 .uav_ref_off => {60 .uav_ref_off => {
56 try uavRefOff(wasm, code, mir.extraData(Mir.UavRefOff, datas[inst].payload).data);61 if (is_obj) {
62 try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32);
63 } else {
64 try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32);
65 }
57 inst += 1;66 inst += 1;
58 continue :loop tags[inst];67 continue :loop tags[inst];
59 },68 },
60 .nav_ref => {69 .nav_ref => {
61 try navRefOff(wasm, code, .{ .ip_index = datas[inst].ip_index, .offset = 0 }, is_wasm32);70 try navRefOff(wasm, code, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32);
62 inst += 1;71 inst += 1;
63 continue :loop tags[inst];72 continue :loop tags[inst];
64 },73 },
...@@ -124,7 +133,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -124,7 +133,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
124 },133 },
125134
126 .br_table => {135 .br_table => {
127 const extra_index = mir.instructions.items(.data)[inst].payload;136 const extra_index = datas[inst].payload;
128 const extra = mir.extraData(Mir.JumpTable, extra_index);137 const extra = mir.extraData(Mir.JumpTable, extra_index);
129 const labels = mir.extra[extra.end..][0..extra.data.length];138 const labels = mir.extra[extra.end..][0..extra.data.length];
130 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);139 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);
...@@ -223,7 +232,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -223,7 +232,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
223 continue :loop tags[inst];232 continue :loop tags[inst];
224 },233 },
225234
226 .global_set => {235 .global_set_sp => {
227 try code.ensureUnusedCapacity(gpa, 6);236 try code.ensureUnusedCapacity(gpa, 6);
228 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));237 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
229 if (is_obj) {238 if (is_obj) {
...@@ -235,7 +244,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -235,7 +244,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
235 });244 });
236 code.appendNTimesAssumeCapacity(0, 5);245 code.appendNTimesAssumeCapacity(0, 5);
237 } else {246 } else {
238 const sp_global = try wasm.stackPointerGlobalIndex();247 const sp_global: Wasm.GlobalIndex = .stack_pointer;
239 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;248 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
240 }249 }
241250
...@@ -243,26 +252,6 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -243,26 +252,6 @@ pub fn lowerToCode(emit: *Emit) Error!void {
243 continue :loop tags[inst];252 continue :loop tags[inst];
244 },253 },
245254
246 .function_index => {
247 try code.ensureUnusedCapacity(gpa, 6);
248 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
249 if (is_obj) {
250 try wasm.out_relocs.append(gpa, .{
251 .offset = @intCast(code.items.len),
252 .pointee = .{ .symbol_index = try wasm.functionSymbolIndex(datas[inst].ip_index) },
253 .tag = .TABLE_INDEX_SLEB,
254 .addend = 0,
255 });
256 code.appendNTimesAssumeCapacity(0, 5);
257 } else {
258 const func_index = try wasm.functionIndex(datas[inst].ip_index);
259 std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(func_index)) catch unreachable;
260 }
261
262 inst += 1;
263 continue :loop tags[inst];
264 },
265
266 .f32_const => {255 .f32_const => {
267 try code.ensureUnusedCapacity(gpa, 5);256 try code.ensureUnusedCapacity(gpa, 5);
268 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));257 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));
...@@ -521,7 +510,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -521,7 +510,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
521 },510 },
522 .simd_prefix => {511 .simd_prefix => {
523 try code.ensureUnusedCapacity(gpa, 6 + 20);512 try code.ensureUnusedCapacity(gpa, 6 + 20);
524 const extra_index = mir.instructions.items(.data)[inst].payload;513 const extra_index = datas[inst].payload;
525 const opcode = mir.extra[extra_index];514 const opcode = mir.extra[extra_index];
526 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));515 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));
527 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;516 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
...@@ -578,7 +567,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -578,7 +567,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
578 .atomics_prefix => {567 .atomics_prefix => {
579 try code.ensureUnusedCapacity(gpa, 6 + 20);568 try code.ensureUnusedCapacity(gpa, 6 + 20);
580569
581 const extra_index = mir.instructions.items(.data)[inst].payload;570 const extra_index = datas[inst].payload;
582 const opcode = mir.extra[extra_index];571 const opcode = mir.extra[extra_index];
583 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));572 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
584 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;573 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;
...@@ -677,34 +666,36 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {...@@ -677,34 +666,36 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {
677 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;666 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
678}667}
679668
680fn uavRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOff, is_wasm32: bool) !void {669fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void {
681 const comp = wasm.base.comp;670 const comp = wasm.base.comp;
682 const gpa = comp.gpa;671 const gpa = comp.gpa;
683 const is_obj = comp.config.output_mode == .Obj;
684 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;672 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
685673
686 try code.ensureUnusedCapacity(gpa, 11);674 try code.ensureUnusedCapacity(gpa, 11);
687 code.appendAssumeCapacity(@intFromEnum(opcode));675 code.appendAssumeCapacity(@intFromEnum(opcode));
688676
689 // If outputting an object file, this needs to be a relocation, since global677 try wasm.out_relocs.append(gpa, .{
690 // constant data may be mixed with other object files in the final link.678 .offset = @intCast(code.items.len),
691 if (is_obj) {679 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) },
692 try wasm.out_relocs.append(gpa, .{680 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,
693 .offset = @intCast(code.items.len),681 .addend = data.offset,
694 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.ip_index) },682 });
695 .tag = if (is_wasm32) .MEMORY_ADDR_LEB else .MEMORY_ADDR_LEB64,683 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
696 .addend = data.offset,684}
697 });685
698 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);686fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void {
699 return;687 const comp = wasm.base.comp;
700 }688 const gpa = comp.gpa;
689 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
690
691 try code.ensureUnusedCapacity(gpa, 11);
692 code.appendAssumeCapacity(@intFromEnum(opcode));
701693
702 // When linking into the final binary, no relocation mechanism is necessary.694 const addr = try wasm.uavAddr(data.uav_exe);
703 const addr = try wasm.uavAddr(data.ip_index);695 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
704 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;
705}696}
706697
707fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {698fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {
708 const comp = wasm.base.comp;699 const comp = wasm.base.comp;
709 const zcu = comp.zcu.?;700 const zcu = comp.zcu.?;
710 const ip = &zcu.intern_pool;701 const ip = &zcu.intern_pool;
...@@ -715,7 +706,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir...@@ -715,7 +706,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir
715 try code.ensureUnusedCapacity(gpa, 11);706 try code.ensureUnusedCapacity(gpa, 11);
716707
717 if (ip.isFunctionType(nav_ty)) {708 if (ip.isFunctionType(nav_ty)) {
718 code.appendAssumeCapacity(std.wasm.Opcode.i32_const);709 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
719 assert(data.offset == 0);710 assert(data.offset == 0);
720 if (is_obj) {711 if (is_obj) {
721 try wasm.out_relocs.append(gpa, .{712 try wasm.out_relocs.append(gpa, .{
...@@ -727,7 +718,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir...@@ -727,7 +718,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir
727 code.appendNTimesAssumeCapacity(0, 5);718 code.appendNTimesAssumeCapacity(0, 5);
728 } else {719 } else {
729 const addr = try wasm.navAddr(data.nav_index);720 const addr = try wasm.navAddr(data.nav_index);
730 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;721 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
731 }722 }
732 } else {723 } else {
733 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;724 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
...@@ -742,7 +733,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir...@@ -742,7 +733,7 @@ fn navRefOff(wasm: *link.File.Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir
742 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);733 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);
743 } else {734 } else {
744 const addr = try wasm.navAddr(data.nav_index);735 const addr = try wasm.navAddr(data.nav_index);
745 leb.writeUleb128(code.fixedWriter(), addr + data.offset) catch unreachable;736 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;
746 }737 }
747 }738 }
748}739}
src/arch/wasm/Mir.zig+14-2
...@@ -610,6 +610,8 @@ pub const Inst = struct {...@@ -610,6 +610,8 @@ pub const Inst = struct {
610 nav_index: InternPool.Nav.Index,610 nav_index: InternPool.Nav.Index,
611 func_ty: Wasm.FunctionType.Index,611 func_ty: Wasm.FunctionType.Index,
612 intrinsic: Intrinsic,612 intrinsic: Intrinsic,
613 uav_obj: Wasm.UavsObjIndex,
614 uav_exe: Wasm.UavsExeIndex,
613615
614 comptime {616 comptime {
615 switch (builtin.mode) {617 switch (builtin.mode) {
...@@ -633,6 +635,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data...@@ -633,6 +635,11 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data
633 inline for (fields) |field| {635 inline for (fields) |field| {
634 @field(result, field.name) = switch (field.type) {636 @field(result, field.name) = switch (field.type) {
635 u32 => self.extra[i],637 u32 => self.extra[i],
638 i32 => @bitCast(self.extra[i]),
639 Wasm.UavsObjIndex,
640 Wasm.UavsExeIndex,
641 InternPool.Nav.Index,
642 => @enumFromInt(self.extra[i]),
636 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),643 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
637 };644 };
638 i += 1;645 i += 1;
...@@ -684,8 +691,13 @@ pub const MemArg = struct {...@@ -684,8 +691,13 @@ pub const MemArg = struct {
684 alignment: u32,691 alignment: u32,
685};692};
686693
687pub const UavRefOff = struct {694pub const UavRefOffObj = struct {
688 ip_index: InternPool.Index,695 uav_obj: Wasm.UavsObjIndex,
696 offset: i32,
697};
698
699pub const UavRefOffExe = struct {
700 uav_exe: Wasm.UavsExeIndex,
689 offset: i32,701 offset: i32,
690};702};
691703
src/codegen.zig+1-3
...@@ -590,7 +590,7 @@ fn lowerPtr(...@@ -590,7 +590,7 @@ fn lowerPtr(
590 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;590 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
591 const offset: u64 = prev_offset + ptr.byte_offset;591 const offset: u64 = prev_offset + ptr.byte_offset;
592 return switch (ptr.base_addr) {592 return switch (ptr.base_addr) {
593 .nav => |nav| try lowerNavRef(bin_file, pt, src_loc, nav, code, reloc_parent, offset),593 .nav => |nav| try lowerNavRef(bin_file, pt, nav, code, reloc_parent, offset),
594 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),594 .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),
595 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),595 .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),
596 .eu_payload => |eu_ptr| try lowerPtr(596 .eu_payload => |eu_ptr| try lowerPtr(
...@@ -708,13 +708,11 @@ fn lowerUavRef(...@@ -708,13 +708,11 @@ fn lowerUavRef(
708fn lowerNavRef(708fn lowerNavRef(
709 lf: *link.File,709 lf: *link.File,
710 pt: Zcu.PerThread,710 pt: Zcu.PerThread,
711 src_loc: Zcu.LazySrcLoc,
712 nav_index: InternPool.Nav.Index,711 nav_index: InternPool.Nav.Index,
713 code: *std.ArrayListUnmanaged(u8),712 code: *std.ArrayListUnmanaged(u8),
714 reloc_parent: link.File.RelocInfo.Parent,713 reloc_parent: link.File.RelocInfo.Parent,
715 offset: u64,714 offset: u64,
716) GenerateSymbolError!void {715) GenerateSymbolError!void {
717 _ = src_loc;
718 const zcu = pt.zcu;716 const zcu = pt.zcu;
719 const gpa = zcu.gpa;717 const gpa = zcu.gpa;
720 const ip = &zcu.intern_pool;718 const ip = &zcu.intern_pool;
src/link/Wasm.zig+435-109
...@@ -119,21 +119,6 @@ object_relocations: std.MultiArrayList(ObjectRelocation) = .empty,...@@ -119,21 +119,6 @@ object_relocations: std.MultiArrayList(ObjectRelocation) = .empty,
119/// by the (synthetic) __wasm_call_ctors function.119/// by the (synthetic) __wasm_call_ctors function.
120object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty,120object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty,
121121
122/// Relocations to be emitted into an object file. Remains empty when not
123/// emitting an object file.
124out_relocs: std.MultiArrayList(OutReloc) = .empty,
125/// List of locations within `string_bytes` that must be patched with the virtual
126/// memory address of a Uav during `flush`.
127/// When emitting an object file, `out_relocs` is used instead.
128uav_fixups: std.ArrayListUnmanaged(UavFixup) = .empty,
129/// List of locations within `string_bytes` that must be patched with the virtual
130/// memory address of a Nav during `flush`.
131/// When emitting an object file, `out_relocs` is used instead.
132nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
133/// Symbols to be emitted into an object file. Remains empty when not emitting
134/// an object file.
135symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
136
137/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.122/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
138object_data_segments: std.ArrayListUnmanaged(DataSegment) = .empty,123object_data_segments: std.ArrayListUnmanaged(DataSegment) = .empty,
139/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.124/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
...@@ -149,6 +134,21 @@ object_total_sections: u32 = 0,...@@ -149,6 +134,21 @@ object_total_sections: u32 = 0,
149/// All comdat symbols from all objects concatenated.134/// All comdat symbols from all objects concatenated.
150object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty,135object_comdat_symbols: std.MultiArrayList(Comdat.Symbol) = .empty,
151136
137/// Relocations to be emitted into an object file. Remains empty when not
138/// emitting an object file.
139out_relocs: std.MultiArrayList(OutReloc) = .empty,
140/// List of locations within `string_bytes` that must be patched with the virtual
141/// memory address of a Uav during `flush`.
142/// When emitting an object file, `out_relocs` is used instead.
143uav_fixups: std.ArrayListUnmanaged(UavFixup) = .empty,
144/// List of locations within `string_bytes` that must be patched with the virtual
145/// memory address of a Nav during `flush`.
146/// When emitting an object file, `out_relocs` is used instead.
147nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
148/// Symbols to be emitted into an object file. Remains empty when not emitting
149/// an object file.
150symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
151
152/// When importing objects from the host environment, a name must be supplied.152/// When importing objects from the host environment, a name must be supplied.
153/// LLVM uses "env" by default when none is given. This would be a good default for Zig153/// LLVM uses "env" by default when none is given. This would be a good default for Zig
154/// to support existing code.154/// to support existing code.
...@@ -170,9 +170,15 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),...@@ -170,9 +170,15 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),
170preloaded_strings: PreloadedStrings,170preloaded_strings: PreloadedStrings,
171171
172/// This field is used when emitting an object; `navs_exe` used otherwise.172/// This field is used when emitting an object; `navs_exe` used otherwise.
173navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, NavObj) = .empty,173navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,
174/// This field is unused when emitting an object; `navs_exe` used otherwise.174/// This field is unused when emitting an object; `navs_exe` used otherwise.
175navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, NavExe) = .empty,175navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,
176/// Tracks all InternPool values referenced by codegen. Needed for outputting
177/// the data segment. This one does not track ref count because object files
178/// require using max LEB encoding for these references anyway.
179uavs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataObj) = .empty,
180/// Tracks ref count to optimize LEB encodings for UAV references.
181uavs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuDataExe) = .empty,
176zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,182zcu_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ZcuFunc) = .empty,
177nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,183nav_exports: std.AutoArrayHashMapUnmanaged(NavExport, Zcu.Export.Index) = .empty,
178uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,184uav_exports: std.AutoArrayHashMapUnmanaged(UavExport, Zcu.Export.Index) = .empty,
...@@ -224,6 +230,13 @@ global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty,...@@ -224,6 +230,13 @@ global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty,
224tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,230tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,
225table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty,231table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty,
226232
233/// Ordered list of data segments that will appear in the final binary.
234/// When sorted, to-be-merged segments will be made adjacent.
235/// Values are offset relative to segment start.
236data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty,
237
238error_name_table_ref_count: u32 = 0,
239
227any_exports_updated: bool = true,240any_exports_updated: bool = true,
228/// Set to true if any `GLOBAL_INDEX` relocation is encountered with241/// Set to true if any `GLOBAL_INDEX` relocation is encountered with
229/// `SymbolFlags.tls` set to true. This is for objects only; final242/// `SymbolFlags.tls` set to true. This is for objects only; final
...@@ -307,6 +320,16 @@ pub const OutputFunctionIndex = enum(u32) {...@@ -307,6 +320,16 @@ pub const OutputFunctionIndex = enum(u32) {
307pub const GlobalIndex = enum(u32) {320pub const GlobalIndex = enum(u32) {
308 _,321 _,
309322
323 /// This is only accurate when there is a Zcu.
324 pub const stack_pointer: GlobalIndex = @enumFromInt(0);
325
326 /// Same as `stack_pointer` but with a safety assertion.
327 pub fn stackPointer(wasm: *const Wasm) Global.Index {
328 const comp = wasm.base.comp;
329 assert(comp.zcu != null);
330 return .stack_pointer;
331 }
332
310 pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {333 pub fn ptr(index: GlobalIndex, f: *const Flush) *Wasm.GlobalImport.Resolution {
311 return &f.globals.items[@intFromEnum(index)];334 return &f.globals.items[@intFromEnum(index)];
312 }335 }
...@@ -545,56 +568,83 @@ pub const Valtype3 = enum(u3) {...@@ -545,56 +568,83 @@ pub const Valtype3 = enum(u3) {
545 }568 }
546};569};
547570
548pub const NavObj = extern struct {571/// Index into `Wasm.navs_obj`.
549 code: DataSegment.Payload,572pub const NavsObjIndex = enum(u32) {
550 /// Empty if not emitting an object.573 _,
551 relocs: OutReloc.Slice,
552574
553 /// Index into `Wasm.navs_obj`.575 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
554 /// Note that swapRemove is sometimes performed on `navs`.576 return &wasm.navs_obj.keys()[@intFromEnum(i)];
555 pub const Index = enum(u32) {577 }
556 _,
557578
558 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {579 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
559 return &wasm.navs_obj.keys()[@intFromEnum(i)];580 return &wasm.navs_obj.values()[@intFromEnum(i)];
560 }581 }
561582
562 pub fn value(i: @This(), wasm: *const Wasm) *NavObj {583 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
563 return &wasm.navs_obj.values()[@intFromEnum(i)];584 const zcu = wasm.base.comp.zcu.?;
564 }585 const ip = &zcu.intern_pool;
586 const nav = ip.getNav(i.key(wasm).*);
587 return nav.fqn.toSlice(ip);
588 }
589};
565590
566 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {591/// Index into `Wasm.navs_exe`.
567 const zcu = wasm.base.comp.zcu.?;592pub const NavsExeIndex = enum(u32) {
568 const ip = &zcu.intern_pool;593 _,
569 const nav = ip.getNav(i.key(wasm).*);594
570 return nav.fqn.toSlice(ip);595 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {
571 }596 return &wasm.navs_exe.keys()[@intFromEnum(i)];
572 };597 }
598
599 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
600 return &wasm.navs_exe.values()[@intFromEnum(i)];
601 }
602
603 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {
604 const zcu = wasm.base.comp.zcu.?;
605 const ip = &zcu.intern_pool;
606 const nav = ip.getNav(i.key(wasm).*);
607 return nav.fqn.toSlice(ip);
608 }
573};609};
574610
575pub const NavExe = extern struct {611/// Index into `Wasm.uavs_obj`.
576 code: DataSegment.Payload,612pub const UavsObjIndex = enum(u32) {
613 _,
577614
578 /// Index into `Wasm.navs_exe`.615 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
579 /// Note that swapRemove is sometimes performed on `navs`.616 return &wasm.uavs_obj.keys()[@intFromEnum(i)];
580 pub const Index = enum(u32) {617 }
581 _,
582618
583 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Nav.Index {619 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataObj {
584 return &wasm.navs_exe.keys()[@intFromEnum(i)];620 return &wasm.uavs_obj.values()[@intFromEnum(i)];
585 }621 }
622};
586623
587 pub fn value(i: @This(), wasm: *const Wasm) *NavExe {624/// Index into `Wasm.uavs_exe`.
588 return &wasm.navs_exe.values()[@intFromEnum(i)];625pub const UavsExeIndex = enum(u32) {
589 }626 _,
590627
591 pub fn name(i: @This(), wasm: *const Wasm) [:0]const u8 {628 pub fn key(i: @This(), wasm: *const Wasm) *InternPool.Index {
592 const zcu = wasm.base.comp.zcu.?;629 return &wasm.uavs_exe.keys()[@intFromEnum(i)];
593 const ip = &zcu.intern_pool;630 }
594 const nav = ip.getNav(i.key(wasm).*);631
595 return nav.fqn.toSlice(ip);632 pub fn value(i: @This(), wasm: *const Wasm) *ZcuDataExe {
596 }633 return &wasm.uavs_exe.values()[@intFromEnum(i)];
597 };634 }
635};
636
637/// Used when emitting a relocatable object.
638pub const ZcuDataObj = extern struct {
639 code: DataSegment.Payload,
640 relocs: OutReloc.Slice,
641};
642
643/// Used when not emitting a relocatable object.
644pub const ZcuDataExe = extern struct {
645 code: DataSegment.Payload,
646 /// Tracks how many references there are for the purposes of sorting data segments.
647 count: u32,
598};648};
599649
600pub const ZcuFunc = extern struct {650pub const ZcuFunc = extern struct {
...@@ -841,8 +891,8 @@ pub const GlobalImport = extern struct {...@@ -841,8 +891,8 @@ pub const GlobalImport = extern struct {
841 __tls_size,891 __tls_size,
842 __zig_error_name_table,892 __zig_error_name_table,
843 object_global: ObjectGlobalIndex,893 object_global: ObjectGlobalIndex,
844 nav_exe: NavExe.Index,894 nav_exe: NavsExeIndex,
845 nav_obj: NavObj.Index,895 nav_obj: NavsObjIndex,
846 };896 };
847897
848 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {898 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
...@@ -1150,28 +1200,211 @@ pub const DataSegment = extern struct {...@@ -1150,28 +1200,211 @@ pub const DataSegment = extern struct {
1150 segment_offset: u32,1200 segment_offset: u32,
1151 section_index: ObjectSectionIndex,1201 section_index: ObjectSectionIndex,
11521202
1203 pub const Category = enum {
1204 /// Thread-local variables.
1205 tls,
1206 /// Data that is not zero initialized and not threadlocal.
1207 data,
1208 /// Zero-initialized. Does not require corresponding bytes in the
1209 /// output file.
1210 zero,
1211 };
1212
1153 pub const Payload = extern struct {1213 pub const Payload = extern struct {
1154 /// Points into string_bytes. No corresponding string_table entry.1214 off: Off,
1155 off: u32,
1156 /// The size in bytes of the data representing the segment within the section.1215 /// The size in bytes of the data representing the segment within the section.
1157 len: u32,1216 len: u32,
11581217
1218 pub const Off = enum(u32) {
1219 /// The payload is all zeroes (bss section).
1220 none = std.math.maxInt(u32),
1221 /// Points into string_bytes. No corresponding string_table entry.
1222 _,
1223
1224 pub fn unwrap(off: Off) ?u32 {
1225 return if (off == .none) null else @intFromEnum(off);
1226 }
1227 };
1228
1159 pub fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 {1229 pub fn slice(p: DataSegment.Payload, wasm: *const Wasm) []const u8 {
1160 assert(p.off != p.len);1230 return wasm.string_bytes.items[p.off.unwrap().?..][0..p.len];
1161 return wasm.string_bytes.items[p.off..][0..p.len];
1162 }1231 }
1163 };1232 };
11641233
1165 /// Index into `Wasm.object_data_segments`.1234 pub const Id = enum(u32) {
1166 pub const Index = enum(u32) {1235 __zig_error_name_table,
1236 /// First, an `ObjectDataSegmentIndex`.
1237 /// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
1238 /// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
1167 _,1239 _,
11681240
1169 pub fn ptr(i: Index, wasm: *const Wasm) *DataSegment {1241 const first_object = @intFromEnum(Id.__zig_error_name_table) + 1;
1170 return &wasm.object_data_segments.items[@intFromEnum(i)];1242
1243 pub const Unpacked = union(enum) {
1244 __zig_error_name_table,
1245 object: ObjectDataSegmentIndex,
1246 uav_exe: UavsExeIndex,
1247 uav_obj: UavsObjIndex,
1248 nav_exe: NavsExeIndex,
1249 nav_obj: NavsObjIndex,
1250 };
1251
1252 pub fn pack(wasm: *const Wasm, unpacked: Unpacked) Id {
1253 return switch (unpacked) {
1254 .__zig_error_name_table => .__zig_error_name_table,
1255 .object => |i| @enumFromInt(first_object + @intFromEnum(i)),
1256 inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)),
1257 .nav_exe => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)),
1258 .nav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_obj.entries.len + @intFromEnum(i)),
1259 };
1260 }
1261
1262 pub fn unpack(id: Id, wasm: *const Wasm) Unpacked {
1263 return switch (id) {
1264 .__zig_error_name_table => .__zig_error_name_table,
1265 _ => {
1266 const object_index = @intFromEnum(id) - first_object;
1267
1268 const uav_index = if (object_index < wasm.object_data_segments.items.len)
1269 return .{ .object = @enumFromInt(object_index) }
1270 else
1271 object_index - wasm.object_data_segments.items.len;
1272
1273 const comp = wasm.base.comp;
1274 const is_obj = comp.config.output_mode == .Obj;
1275 if (is_obj) {
1276 const nav_index = if (uav_index < wasm.uavs_obj.entries.len)
1277 return .{ .uav_obj = @enumFromInt(uav_index) }
1278 else
1279 uav_index - wasm.uavs_obj.entries.len;
1280
1281 return .{ .nav_obj = @enumFromInt(nav_index) };
1282 } else {
1283 const nav_index = if (uav_index < wasm.uavs_exe.entries.len)
1284 return .{ .uav_obj = @enumFromInt(uav_index) }
1285 else
1286 uav_index - wasm.uavs_exe.entries.len;
1287
1288 return .{ .nav_exe = @enumFromInt(nav_index) };
1289 }
1290 },
1291 };
1292 }
1293
1294 pub fn category(id: Id, wasm: *const Wasm) Category {
1295 return switch (unpack(id, wasm)) {
1296 .__zig_error_name_table => .data,
1297 .object => |i| {
1298 const ptr = i.ptr(wasm);
1299 if (ptr.flags.tls) return .tls;
1300 if (isBss(wasm, ptr.name)) return .zero;
1301 return .data;
1302 },
1303 inline .uav_exe, .uav_obj => |i| if (i.value(wasm).code.off == .none) .zero else .data,
1304 inline .nav_exe, .nav_obj => |i| {
1305 const zcu = wasm.base.comp.zcu.?;
1306 const ip = &zcu.intern_pool;
1307 const nav = ip.getNav(i.key(wasm).*);
1308 if (nav.isThreadLocal(ip)) return .tls;
1309 const code = i.value(wasm).code;
1310 return if (code.off == .none) .zero else .data;
1311 },
1312 };
1313 }
1314
1315 pub fn isTls(id: Id, wasm: *const Wasm) bool {
1316 return switch (unpack(id, wasm)) {
1317 .__zig_error_name_table => false,
1318 .object => |i| i.ptr(wasm).flags.tls,
1319 .uav_exe, .uav_obj => false,
1320 inline .nav_exe, .nav_obj => |i| {
1321 const zcu = wasm.base.comp.zcu.?;
1322 const ip = &zcu.intern_pool;
1323 const nav = ip.getNav(i.key(wasm).*);
1324 return nav.isThreadLocal(ip);
1325 },
1326 };
1327 }
1328
1329 pub fn name(id: Id, wasm: *const Wasm) []const u8 {
1330 return switch (unpack(id, wasm)) {
1331 .__zig_error_name_table, .uav_exe, .uav_obj => ".data",
1332 .object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),
1333 inline .nav_exe, .nav_obj => |i| {
1334 const zcu = wasm.base.comp.zcu.?;
1335 const ip = &zcu.intern_pool;
1336 const nav = ip.getNav(i.key(wasm).*);
1337 return nav.status.resolved.@"linksection".toSlice(ip) orelse ".data";
1338 },
1339 };
1340 }
1341
1342 pub fn alignment(id: Id, wasm: *const Wasm) Alignment {
1343 return switch (unpack(id, wasm)) {
1344 .__zig_error_name_table => wasm.pointerAlignment(),
1345 .object => |i| i.ptr(wasm).flags.alignment,
1346 inline .uav_exe, .uav_obj => |i| {
1347 const zcu = wasm.base.comp.zcu.?;
1348 const ip = &zcu.intern_pool;
1349 const ip_index = i.key(wasm).*;
1350 const ty: ZcuType = .fromInterned(ip.typeOf(ip_index));
1351 return ty.abiAlignment(zcu);
1352 },
1353 inline .nav_exe, .nav_obj => |i| {
1354 const zcu = wasm.base.comp.zcu.?;
1355 const ip = &zcu.intern_pool;
1356 const nav = ip.getNav(i.key(wasm).*);
1357 return nav.status.resolved.alignment;
1358 },
1359 };
1360 }
1361
1362 pub fn refCount(id: Id, wasm: *const Wasm) u32 {
1363 return switch (unpack(id, wasm)) {
1364 .__zig_error_name_table => wasm.error_name_table_ref_count,
1365 .object, .uav_obj, .nav_obj => 0,
1366 inline .uav_exe, .nav_exe => |i| i.value(wasm).count,
1367 };
1368 }
1369
1370 pub fn isPassive(id: Id, wasm: *const Wasm) bool {
1371 return switch (unpack(id, wasm)) {
1372 .__zig_error_name_table => true,
1373 .object => |i| i.ptr(wasm).flags.is_passive,
1374 .uav_exe, .uav_obj, .nav_exe, .nav_obj => true,
1375 };
1376 }
1377
1378 pub fn size(id: Id, wasm: *const Wasm) u32 {
1379 return switch (unpack(id, wasm)) {
1380 .__zig_error_name_table => {
1381 const comp = wasm.base.comp;
1382 const zcu = comp.zcu.?;
1383 const errors_len = 1 + zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
1384 const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu);
1385 return @intCast(errors_len * elem_size);
1386 },
1387 .object => |i| i.ptr(wasm).payload.len,
1388 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len,
1389 };
1171 }1390 }
1172 };1391 };
1173};1392};
11741393
1394/// Index into `Wasm.object_data_segments`.
1395pub const ObjectDataSegmentIndex = enum(u32) {
1396 _,
1397
1398 pub fn ptr(i: ObjectDataSegmentIndex, wasm: *const Wasm) *DataSegment {
1399 return &wasm.object_data_segments.items[@intFromEnum(i)];
1400 }
1401};
1402
1403/// Index into `Wasm.uavs`.
1404pub const UavIndex = enum(u32) {
1405 _,
1406};
1407
1175pub const CustomSegment = extern struct {1408pub const CustomSegment = extern struct {
1176 payload: Payload,1409 payload: Payload,
1177 flags: SymbolFlags,1410 flags: SymbolFlags,
...@@ -1940,6 +2173,8 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1940,6 +2173,8 @@ pub fn deinit(wasm: *Wasm) void {
19402173
1941 wasm.navs_exe.deinit(gpa);2174 wasm.navs_exe.deinit(gpa);
1942 wasm.navs_obj.deinit(gpa);2175 wasm.navs_obj.deinit(gpa);
2176 wasm.uavs_exe.deinit(gpa);
2177 wasm.uavs_obj.deinit(gpa);
1943 wasm.zcu_funcs.deinit(gpa);2178 wasm.zcu_funcs.deinit(gpa);
1944 wasm.nav_exports.deinit(gpa);2179 wasm.nav_exports.deinit(gpa);
1945 wasm.uav_exports.deinit(gpa);2180 wasm.uav_exports.deinit(gpa);
...@@ -1978,6 +2213,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1978,6 +2213,7 @@ pub fn deinit(wasm: *Wasm) void {
1978 wasm.global_exports.deinit(gpa);2213 wasm.global_exports.deinit(gpa);
1979 wasm.global_imports.deinit(gpa);2214 wasm.global_imports.deinit(gpa);
1980 wasm.table_imports.deinit(gpa);2215 wasm.table_imports.deinit(gpa);
2216 wasm.data_segments.deinit(gpa);
1981 wasm.symbol_table.deinit(gpa);2217 wasm.symbol_table.deinit(gpa);
1982 wasm.out_relocs.deinit(gpa);2218 wasm.out_relocs.deinit(gpa);
1983 wasm.uav_fixups.deinit(gpa);2219 wasm.uav_fixups.deinit(gpa);
...@@ -2053,57 +2289,28 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2053,57 +2289,28 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2053 return;2289 return;
2054 }2290 }
20552291
2056 const code_start: u32 = @intCast(wasm.string_bytes.items.len);2292 const zcu_data = try lowerZcuData(wasm, pt, nav_init);
2057 const relocs_start: u32 = @intCast(wasm.out_relocs.len);
2058 wasm.string_bytes_lock.lock();
2059
2060 try codegen.generateSymbol(
2061 &wasm.base,
2062 pt,
2063 zcu.navSrcLoc(nav_index),
2064 Value.fromInterned(nav_init),
2065 &wasm.string_bytes,
2066 .none,
2067 );
2068
2069 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
2070 const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start);
2071 wasm.string_bytes_lock.unlock();
20722293
2073 const naive_code: DataSegment.Payload = .{2294 try wasm.data_segments.ensureUnusedCapacity(gpa, 1);
2074 .off = code_start,
2075 .len = code_len,
2076 };
2077
2078 // Only nonzero init values need to take up space in the output.
2079 const all_zeroes = std.mem.allEqual(u8, naive_code.slice(wasm), 0);
2080 const code: DataSegment.Payload = if (!all_zeroes) naive_code else c: {
2081 wasm.string_bytes.shrinkRetainingCapacity(code_start);
2082 // Indicate empty by making off and len the same value, however, still
2083 // transmit the data size by using the size as that value.
2084 break :c .{
2085 .off = naive_code.len,
2086 .len = naive_code.len,
2087 };
2088 };
20892295
2090 if (is_obj) {2296 if (is_obj) {
2091 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);2297 const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
2092 gop.value_ptr.* = .{2298 gop.value_ptr.* = zcu_data;
2093 .code = code,2299 wasm.data_segments.putAssumeCapacity(.pack(wasm, .{
2094 .relocs = .{2300 .nav_obj = @enumFromInt(gop.index),
2095 .off = relocs_start,2301 }), @as(u32, undefined));
2096 .len = relocs_len,
2097 },
2098 };
2099 }2302 }
21002303
2101 assert(relocs_len == 0);2304 assert(zcu_data.relocs.len == 0);
21022305
2103 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);2306 const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
2104 gop.value_ptr.* = .{2307 gop.value_ptr.* = .{
2105 .code = code,2308 .code = zcu_data.code,
2309 .count = 0,
2106 };2310 };
2311 wasm.data_segments.putAssumeCapacity(.pack(wasm, .{
2312 .nav_exe = @enumFromInt(gop.index),
2313 }), @as(u32, undefined));
2107}2314}
21082315
2109pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {2316pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
...@@ -2251,6 +2458,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v...@@ -2251,6 +2458,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
2251 }2458 }
2252 }2459 }
22532460
2461 if (comp.zcu != null) {
2462 // Zig always depends on a stack pointer global.
2463 try wasm.globals.put(gpa, .__stack_pointer, {});
2464 assert(wasm.globals.entries.len - 1 == @intFromEnum(GlobalIndex.stack_pointer));
2465 }
2466
2254 // These loops do both recursive marking of alive symbols well as checking for undefined symbols.2467 // These loops do both recursive marking of alive symbols well as checking for undefined symbols.
2255 // At the end, output functions and globals will be populated.2468 // At the end, output functions and globals will be populated.
2256 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {2469 for (wasm.object_function_imports.keys(), wasm.object_function_imports.values(), 0..) |name, *import, i| {
...@@ -2468,6 +2681,9 @@ pub fn flushModule(...@@ -2468,6 +2681,9 @@ pub fn flushModule(
2468 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);2681 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);
2469 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);2682 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);
24702683
2684 const data_segments_end_zcu: u32 = @intCast(wasm.data_segments.entries.len);
2685 defer wasm.data_segments.shrinkRetainingCapacity(data_segments_end_zcu);
2686
2471 wasm.flush_buffer.clear();2687 wasm.flush_buffer.clear();
24722688
2473 return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {2689 return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {
...@@ -2999,7 +3215,7 @@ pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error...@@ -2999,7 +3215,7 @@ pub fn addRelocatableDataPayload(wasm: *Wasm, bytes: []const u8) Allocator.Error
2999 const gpa = wasm.base.comp.gpa;3215 const gpa = wasm.base.comp.gpa;
3000 try wasm.string_bytes.appendSlice(gpa, bytes);3216 try wasm.string_bytes.appendSlice(gpa, bytes);
3001 return .{3217 return .{
3002 .off = @intCast(wasm.string_bytes.items.len - bytes.len),3218 .off = @enumFromInt(wasm.string_bytes.items.len - bytes.len),
3003 .len = @intCast(bytes.len),3219 .len = @intCast(bytes.len),
3004 };3220 };
3005}3221}
...@@ -3025,6 +3241,65 @@ pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Er...@@ -3025,6 +3241,65 @@ pub fn navSymbolIndex(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Er
3025 return @enumFromInt(gop.index);3241 return @enumFromInt(gop.index);
3026}3242}
30273243
3244pub fn errorNameTableSymbolIndex(wasm: *Wasm) Allocator.Error!SymbolTableIndex {
3245 const comp = wasm.base.comp;
3246 assert(comp.config.output_mode == .Obj);
3247 const gpa = comp.gpa;
3248 const gop = try wasm.symbol_table.getOrPut(gpa, wasm.preloaded_strings.__zig_error_name_table);
3249 gop.value_ptr.* = {};
3250 return @enumFromInt(gop.index);
3251}
3252
3253pub fn refUavObj(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !UavsObjIndex {
3254 const comp = wasm.base.comp;
3255 const gpa = comp.gpa;
3256 assert(comp.config.output_mode == .Obj);
3257 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
3258 if (!gop.found_existing) gop.value_ptr.* = try lowerZcuData(wasm, pt, ip_index);
3259 const uav_index: UavsObjIndex = @enumFromInt(gop.index);
3260 try wasm.data_segments.put(gpa, .pack(wasm, .{ .uav_obj = uav_index }), @as(u32, undefined));
3261 return uav_index;
3262}
3263
3264pub fn refUavExe(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !UavsExeIndex {
3265 const comp = wasm.base.comp;
3266 const gpa = comp.gpa;
3267 assert(comp.config.output_mode != .Obj);
3268 const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index);
3269 if (gop.found_existing) {
3270 gop.value_ptr.count += 1;
3271 } else {
3272 const zcu_data = try lowerZcuData(wasm, pt, ip_index);
3273 gop.value_ptr.* = .{
3274 .code = zcu_data.code,
3275 .count = 1,
3276 };
3277 }
3278 const uav_index: UavsExeIndex = @enumFromInt(gop.index);
3279 wasm.data_segments.putAssumeCapacity(.pack(wasm, .{ .uav_exe = uav_index }), @as(u32, undefined));
3280 return uav_index;
3281}
3282
3283pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) Allocator.Error!u32 {
3284 const comp = wasm.base.comp;
3285 assert(comp.config.output_mode != .Obj);
3286 const ds_id: DataSegment.Id = .pack(wasm, .{ .uav_exe = uav_index });
3287 return wasm.data_segments.get(ds_id).?;
3288}
3289
3290pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) Allocator.Error!u32 {
3291 const comp = wasm.base.comp;
3292 assert(comp.config.output_mode != .Obj);
3293 const ds_id: DataSegment.Id = .pack(wasm, .{ .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?) });
3294 return wasm.data_segments.get(ds_id).?;
3295}
3296
3297pub fn errorNameTableAddr(wasm: *Wasm) Allocator.Error!u32 {
3298 const comp = wasm.base.comp;
3299 assert(comp.config.output_mode != .Obj);
3300 return wasm.data_segments.get(.__zig_error_name_table).?;
3301}
3302
3028fn convertZcuFnType(3303fn convertZcuFnType(
3029 comp: *Compilation,3304 comp: *Compilation,
3030 cc: std.builtin.CallingConvention,3305 cc: std.builtin.CallingConvention,
...@@ -3080,3 +3355,54 @@ fn convertZcuFnType(...@@ -3080,3 +3355,54 @@ fn convertZcuFnType(
3080 }3355 }
3081 }3356 }
3082}3357}
3358
3359pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {
3360 const s = optional_name.slice(wasm) orelse return false;
3361 return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
3362}
3363
3364fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj {
3365 const code_start: u32 = @intCast(wasm.string_bytes.items.len);
3366 const relocs_start: u32 = @intCast(wasm.out_relocs.len);
3367 wasm.string_bytes_lock.lock();
3368
3369 try codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &wasm.string_bytes, .none);
3370
3371 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
3372 const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start);
3373 wasm.string_bytes_lock.unlock();
3374
3375 const naive_code: DataSegment.Payload = .{
3376 .off = @enumFromInt(code_start),
3377 .len = code_len,
3378 };
3379
3380 // Only nonzero init values need to take up space in the output.
3381 const all_zeroes = std.mem.allEqual(u8, naive_code.slice(wasm), 0);
3382 const code: DataSegment.Payload = if (!all_zeroes) naive_code else c: {
3383 wasm.string_bytes.shrinkRetainingCapacity(code_start);
3384 // Indicate empty by making off and len the same value, however, still
3385 // transmit the data size by using the size as that value.
3386 break :c .{
3387 .off = .none,
3388 .len = naive_code.len,
3389 };
3390 };
3391
3392 return .{
3393 .code = code,
3394 .relocs = .{
3395 .off = relocs_start,
3396 .len = relocs_len,
3397 },
3398 };
3399}
3400
3401fn pointerAlignment(wasm: *const Wasm) Alignment {
3402 const target = &wasm.base.comp.root_mod.resolved_target.result;
3403 return switch (target.cpu.arch) {
3404 .wasm32 => .@"4",
3405 .wasm64 => .@"8",
3406 else => unreachable,
3407 };
3408}
src/link/Wasm/Flush.zig+93-75
...@@ -19,10 +19,6 @@ const leb = std.leb;...@@ -19,10 +19,6 @@ const leb = std.leb;
19const log = std.log.scoped(.link);19const log = std.log.scoped(.link);
20const assert = std.debug.assert;20const assert = std.debug.assert;
2121
22/// Ordered list of data segments that will appear in the final binary.
23/// When sorted, to-be-merged segments will be made adjacent.
24/// Values are offset relative to segment start.
25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Index, u32) = .empty,
26/// Each time a `data_segment` offset equals zero it indicates a new group, and22/// Each time a `data_segment` offset equals zero it indicates a new group, and
27/// the next element in this array will contain the total merged segment size.23/// the next element in this array will contain the total merged segment size.
28data_segment_groups: std.ArrayListUnmanaged(u32) = .empty,24data_segment_groups: std.ArrayListUnmanaged(u32) = .empty,
...@@ -32,21 +28,14 @@ missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,...@@ -32,21 +28,14 @@ missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
3228
33indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, u32) = .empty,29indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, u32) = .empty,
3430
35/// 0. Index into `data_segments`.
36const DataSegmentIndex = enum(u32) {
37 _,
38};
39
40pub fn clear(f: *Flush) void {31pub fn clear(f: *Flush) void {
41 f.binary_bytes.clearRetainingCapacity();32 f.binary_bytes.clearRetainingCapacity();
42 f.data_segments.clearRetainingCapacity();
43 f.data_segment_groups.clearRetainingCapacity();33 f.data_segment_groups.clearRetainingCapacity();
44 f.indirect_function_table.clearRetainingCapacity();34 f.indirect_function_table.clearRetainingCapacity();
45}35}
4636
47pub fn deinit(f: *Flush, gpa: Allocator) void {37pub fn deinit(f: *Flush, gpa: Allocator) void {
48 f.binary_bytes.deinit(gpa);38 f.binary_bytes.deinit(gpa);
49 f.data_segments.deinit(gpa);
50 f.data_segment_groups.deinit(gpa);39 f.data_segment_groups.deinit(gpa);
51 f.indirect_function_table.deinit(gpa);40 f.indirect_function_table.deinit(gpa);
52 f.* = undefined;41 f.* = undefined;
...@@ -141,12 +130,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -141,12 +130,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
141130
142 // Merge and order the data segments. Depends on garbage collection so that131 // Merge and order the data segments. Depends on garbage collection so that
143 // unused segments can be omitted.132 // unused segments can be omitted.
144 try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len);133 try wasm.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len);
145 for (wasm.object_data_segments.items, 0..) |*ds, i| {134 for (wasm.object_data_segments.items, 0..) |*ds, i| {
146 if (!ds.flags.alive) continue;135 if (!ds.flags.alive) continue;
147 const data_segment_index: Wasm.DataSegment.Index = @enumFromInt(i);136 const data_segment_index: Wasm.ObjectDataSegmentIndex = @enumFromInt(i);
148 any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !isBss(wasm, ds.name));137 any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !wasm.isBss(ds.name));
149 f.data_segments.putAssumeCapacityNoClobber(data_segment_index, @as(u32, undefined));138 wasm.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
139 .object = data_segment_index,
140 }), @as(u32, undefined));
150 }141 }
151142
152 try wasm.functions.ensureUnusedCapacity(gpa, 3);143 try wasm.functions.ensureUnusedCapacity(gpa, 3);
...@@ -170,48 +161,64 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -170,48 +161,64 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
170 }161 }
171162
172 // Sort order:163 // Sort order:
173 // 0. Whether the segment is TLS164 // 0. Segment category (tls, data, zero)
174 // 1. Segment name prefix165 // 1. Segment name prefix
175 // 2. Segment alignment166 // 2. Segment alignment
176 // 3. Segment name suffix167 // 3. Reference count, descending (optimize for LEB encoding)
177 // 4. Segment index (to break ties, keeping it deterministic)168 // 4. Segment name suffix
169 // 5. Segment ID interpreted as an integer (for determinism)
170 //
178 // TLS segments are intended to be merged with each other, and segments171 // TLS segments are intended to be merged with each other, and segments
179 // with a common prefix name are intended to be merged with each other.172 // with a common prefix name are intended to be merged with each other.
180 // Sorting ensures the segments intended to be merged will be adjacent.173 // Sorting ensures the segments intended to be merged will be adjacent.
174 //
175 // Each Zcu Nav and Cau has an independent data segment ID in this logic.
176 // For the purposes of sorting, they are implicitly all named ".data".
181 const Sort = struct {177 const Sort = struct {
182 wasm: *const Wasm,178 wasm: *const Wasm,
183 segments: []const Wasm.DataSegment.Index,179 segments: []const Wasm.DataSegment.Id,
184 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {180 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
185 const lhs_segment_index = ctx.segments[lhs];181 const lhs_segment = ctx.segments[lhs];
186 const rhs_segment_index = ctx.segments[rhs];182 const rhs_segment = ctx.segments[rhs];
187 const lhs_segment = lhs_segment_index.ptr(ctx.wasm);183 const lhs_category = @intFromEnum(lhs_segment.category(ctx.wasm));
188 const rhs_segment = rhs_segment_index.ptr(ctx.wasm);184 const rhs_category = @intFromEnum(rhs_segment.category(ctx.wasm));
189 const lhs_tls = @intFromBool(lhs_segment.flags.tls);185 switch (std.math.order(lhs_category, rhs_category)) {
190 const rhs_tls = @intFromBool(rhs_segment.flags.tls);186 .lt => return true,
191 if (lhs_tls < rhs_tls) return true;187 .gt => return false,
192 if (lhs_tls > rhs_tls) return false;188 .eq => {},
193 const lhs_prefix, const lhs_suffix = splitSegmentName(lhs_segment.name.unwrap().?.slice(ctx.wasm));189 }
194 const rhs_prefix, const rhs_suffix = splitSegmentName(rhs_segment.name.unwrap().?.slice(ctx.wasm));190 const lhs_segment_name = lhs_segment.name(ctx.wasm);
191 const rhs_segment_name = rhs_segment.name(ctx.wasm);
192 const lhs_prefix, const lhs_suffix = splitSegmentName(lhs_segment_name);
193 const rhs_prefix, const rhs_suffix = splitSegmentName(rhs_segment_name);
195 switch (mem.order(u8, lhs_prefix, rhs_prefix)) {194 switch (mem.order(u8, lhs_prefix, rhs_prefix)) {
196 .lt => return true,195 .lt => return true,
197 .gt => return false,196 .gt => return false,
198 .eq => {},197 .eq => {},
199 }198 }
200 switch (lhs_segment.flags.alignment.order(rhs_segment.flags.alignment)) {199 const lhs_alignment = lhs_segment.alignment(ctx.wasm);
200 const rhs_alignment = rhs_segment.alignment(ctx.wasm);
201 switch (lhs_alignment.order(rhs_alignment)) {
201 .lt => return false,202 .lt => return false,
202 .gt => return true,203 .gt => return true,
203 .eq => {},204 .eq => {},
204 }205 }
205 return switch (mem.order(u8, lhs_suffix, rhs_suffix)) {206 switch (std.math.order(lhs_segment.refCount(ctx.wasm), rhs_segment.refCount(ctx.wasm))) {
206 .lt => true,207 .lt => return false,
207 .gt => false,208 .gt => return true,
208 .eq => @intFromEnum(lhs_segment_index) < @intFromEnum(rhs_segment_index),209 .eq => {},
209 };210 }
211 switch (mem.order(u8, lhs_suffix, rhs_suffix)) {
212 .lt => return true,
213 .gt => return false,
214 .eq => {},
215 }
216 return @intFromEnum(lhs_segment) < @intFromEnum(rhs_segment);
210 }217 }
211 };218 };
212 f.data_segments.sortUnstable(@as(Sort, .{219 wasm.data_segments.sortUnstable(@as(Sort, .{
213 .wasm = wasm,220 .wasm = wasm,
214 .segments = f.data_segments.keys(),221 .segments = wasm.data_segments.keys(),
215 }));222 }));
216223
217 const page_size = std.wasm.page_size; // 64kb224 const page_size = std.wasm.page_size; // 64kb
...@@ -246,43 +253,44 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -246,43 +253,44 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
246 virtual_addrs.stack_pointer = @intCast(memory_ptr);253 virtual_addrs.stack_pointer = @intCast(memory_ptr);
247 }254 }
248255
249 const segment_indexes = f.data_segments.keys();256 const segment_ids = wasm.data_segments.keys();
250 const segment_offsets = f.data_segments.values();257 const segment_offsets = wasm.data_segments.values();
251 assert(f.data_segment_groups.items.len == 0);258 assert(f.data_segment_groups.items.len == 0);
252 {259 {
253 var seen_tls: enum { before, during, after } = .before;260 var seen_tls: enum { before, during, after } = .before;
254 var offset: u32 = 0;261 var offset: u32 = 0;
255 for (segment_indexes, segment_offsets, 0..) |segment_index, *segment_offset, i| {262 for (segment_ids, segment_offsets, 0..) |segment_id, *segment_offset, i| {
256 const segment = segment_index.ptr(wasm);263 const alignment = segment_id.alignment(wasm);
257 memory_ptr = segment.flags.alignment.forward(memory_ptr);264 memory_ptr = alignment.forward(memory_ptr);
258265
259 const want_new_segment = b: {266 const want_new_segment = b: {
260 if (is_obj) break :b false;267 if (is_obj) break :b false;
261 switch (seen_tls) {268 switch (seen_tls) {
262 .before => if (segment.flags.tls) {269 .before => if (segment_id.isTls(wasm)) {
263 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(memory_ptr);270 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(memory_ptr);
264 virtual_addrs.tls_align = segment.flags.alignment;271 virtual_addrs.tls_align = alignment;
265 seen_tls = .during;272 seen_tls = .during;
266 break :b true;273 break :b true;
267 },274 },
268 .during => if (!segment.flags.tls) {275 .during => if (!segment_id.isTls(wasm)) {
269 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);276 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);
270 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(segment.flags.alignment);277 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);
271 seen_tls = .after;278 seen_tls = .after;
272 break :b true;279 break :b true;
273 },280 },
274 .after => {},281 .after => {},
275 }282 }
276 break :b i >= 1 and !wantSegmentMerge(wasm, segment_indexes[i - 1], segment_index);283 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id);
277 };284 };
278 if (want_new_segment) {285 if (want_new_segment) {
279 if (offset > 0) try f.data_segment_groups.append(gpa, offset);286 if (offset > 0) try f.data_segment_groups.append(gpa, offset);
280 offset = 0;287 offset = 0;
281 }288 }
282289
290 const size = segment_id.size(wasm);
283 segment_offset.* = offset;291 segment_offset.* = offset;
284 offset += segment.payload.len;292 offset += size;
285 memory_ptr += segment.payload.len;293 memory_ptr += size;
286 }294 }
287 if (offset > 0) try f.data_segment_groups.append(gpa, offset);295 if (offset > 0) try f.data_segment_groups.append(gpa, offset);
288 }296 }
...@@ -599,7 +607,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -599,7 +607,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
599 // Code section.607 // Code section.
600 if (wasm.functions.count() != 0) {608 if (wasm.functions.count() != 0) {
601 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);609 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
602 const start_offset = binary_bytes.items.len - 5; // minus 5 so start offset is 5 to include entry count
603610
604 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {611 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {
605 .unresolved => unreachable,612 .unresolved => unreachable,
...@@ -610,21 +617,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -610,21 +617,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
610 .__zig_error_names => @panic("TODO lower __zig_error_names "),617 .__zig_error_names => @panic("TODO lower __zig_error_names "),
611 .object_function => |i| {618 .object_function => |i| {
612 _ = i;619 _ = i;
613 _ = start_offset;
614 @panic("TODO lower object function code and apply relocations");620 @panic("TODO lower object function code and apply relocations");
615 //try leb.writeUleb128(binary_writer, atom.code.len);621 //try leb.writeUleb128(binary_writer, atom.code.len);
616 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));622 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
617 },623 },
618 .zcu_func => |i| {624 .zcu_func => |i| {
619 _ = i;625 const code_start = try reserveSize(gpa, binary_bytes);
620 _ = start_offset;626 defer replaceSize(binary_bytes, code_start);
621 @panic("TODO lower zcu_func code and apply relocations");627
622 //try leb.writeUleb128(binary_writer, atom.code.len);628 const function = &i.value(wasm).function;
623 //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));629 try function.lower(wasm, binary_bytes);
624 },630 },
625 };631 };
626632
627 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));633 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
634 if (is_obj) @panic("TODO apply offset to code relocs");
628 code_section_index = section_index;635 code_section_index = section_index;
629 section_index += 1;636 section_index += 1;
630 }637 }
...@@ -635,11 +642,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -635,11 +642,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
635642
636 var group_index: u32 = 0;643 var group_index: u32 = 0;
637 var offset: u32 = undefined;644 var offset: u32 = undefined;
638 for (segment_indexes, segment_offsets) |segment_index, segment_offset| {645 for (segment_ids, segment_offsets) |segment_id, segment_offset| {
639 const segment = segment_index.ptr(wasm);646 const segment = segment_id.ptr(wasm);
640 const segment_payload = segment.payload.slice(wasm);647 const segment_payload = segment.payload.slice(wasm);
641 if (segment_payload.len == 0) continue;648 if (segment_payload.len == 0) continue;
642 if (!import_memory and isBss(wasm, segment.name)) {649 if (!import_memory and wasm.isBss(segment.name)) {
643 // It counted for virtual memory but it does not go into the binary.650 // It counted for virtual memory but it does not go into the binary.
644 continue;651 continue;
645 }652 }
...@@ -682,7 +689,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -682,7 +689,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
682 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);689 // try wasm.emitDataRelocations(binary_bytes, data_index, symbol_table);
683 //}690 //}
684 } else if (comp.config.debug_format != .strip) {691 } else if (comp.config.debug_format != .strip) {
685 try emitNameSection(wasm, &f.data_segments, binary_bytes);692 try emitNameSection(wasm, &wasm.data_segments, binary_bytes);
686 }693 }
687694
688 if (comp.config.debug_format != .strip) {695 if (comp.config.debug_format != .strip) {
...@@ -997,27 +1004,23 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)...@@ -997,27 +1004,23 @@ fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)
997// writeCustomSectionHeader(binary_bytes, header_offset);1004// writeCustomSectionHeader(binary_bytes, header_offset);
998//}1005//}
9991006
1000fn isBss(wasm: *Wasm, optional_name: Wasm.OptionalString) bool {
1001 const s = optional_name.slice(wasm) orelse return false;
1002 return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
1003}
1004
1005fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {1007fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {
1006 const start = @intFromBool(name.len >= 1 and name[0] == '.');1008 const start = @intFromBool(name.len >= 1 and name[0] == '.');
1007 const pivot = mem.indexOfScalarPos(u8, name, start, '.') orelse 0;1009 const pivot = mem.indexOfScalarPos(u8, name, start, '.') orelse 0;
1008 return .{ name[0..pivot], name[pivot..] };1010 return .{ name[0..pivot], name[pivot..] };
1009}1011}
10101012
1011fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index: Wasm.DataSegment.Index) bool {1013fn wantSegmentMerge(wasm: *const Wasm, a_id: Wasm.DataSegment.Id, b_id: Wasm.DataSegment.Id) bool {
1012 const a = a_index.ptr(wasm);1014 const a_category = a_id.category(wasm);
1013 const b = b_index.ptr(wasm);1015 const b_category = b_id.category(wasm);
1014 if (a.flags.tls and b.flags.tls) return true;1016 if (a_category != b_category) return false;
1015 if (a.flags.tls != b.flags.tls) return false;1017 if (a_category == .tls or b_category == .tls) return false;
1016 if (a.flags.is_passive != b.flags.is_passive) return false;1018 if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false;
1017 if (a.name == b.name) return true;1019 const a_name = a_id.name(wasm);
1018 const a_prefix, _ = splitSegmentName(a.name.slice(wasm).?);1020 const b_name = b_id.name(wasm);
1019 const b_prefix, _ = splitSegmentName(b.name.slice(wasm).?);1021 const a_prefix, _ = splitSegmentName(a_name);
1020 return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);1022 const b_prefix, _ = splitSegmentName(b_name);
1023 return mem.eql(u8, a_prefix, b_prefix);
1021}1024}
10221025
1023/// section id + fixed leb contents size + fixed leb vector length1026/// section id + fixed leb contents size + fixed leb vector length
...@@ -1064,6 +1067,21 @@ fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void...@@ -1064,6 +1067,21 @@ fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void
1064 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());1067 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
1065}1068}
10661069
1070const max_size_encoding = 5;
1071
1072fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1073 try bytes.appendNTimes(gpa, 0, max_size_encoding);
1074 return @intCast(bytes.items.len - max_size_encoding);
1075}
1076
1077fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {
1078 const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding);
1079 var buf: [max_size_encoding]u8 = undefined;
1080 var fbw = std.io.fixedBufferStream(&buf);
1081 leb.writeUleb128(fbw.writer(), size) catch unreachable;
1082 bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten());
1083}
1084
1067fn emitLimits(1085fn emitLimits(
1068 gpa: Allocator,1086 gpa: Allocator,
1069 binary_bytes: *std.ArrayListUnmanaged(u8),1087 binary_bytes: *std.ArrayListUnmanaged(u8),
src/link/Wasm/Object.zig+2-2
...@@ -103,7 +103,7 @@ pub const Symbol = struct {...@@ -103,7 +103,7 @@ pub const Symbol = struct {
103 function: Wasm.ObjectFunctionIndex,103 function: Wasm.ObjectFunctionIndex,
104 function_import: ScratchSpace.FuncImportIndex,104 function_import: ScratchSpace.FuncImportIndex,
105 data: struct {105 data: struct {
106 segment_index: Wasm.DataSegment.Index,106 segment_index: Wasm.ObjectDataSegmentIndex,
107 segment_offset: u32,107 segment_offset: u32,
108 size: u32,108 size: u32,
109 },109 },
...@@ -497,7 +497,7 @@ pub fn parse(...@@ -497,7 +497,7 @@ pub fn parse(
497497
498 try wasm.object_custom_segments.put(gpa, section_index, .{498 try wasm.object_custom_segments.put(gpa, section_index, .{
499 .payload = .{499 .payload = .{
500 .off = data_off,500 .off = @enumFromInt(data_off),
501 .len = @intCast(debug_content.len),501 .len = @intCast(debug_content.len),
502 },502 },
503 .flags = .{},503 .flags = .{},