authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-11 22:18:53-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:35-08:00
logbf880595916cad2a21fca2834e334c2b0ac09f24
tree223e2737f73973ab4a4ed5395963c252e507245a
parente21a42723b9acefd27c142bf776a8321621296b8

wasm linker: flush implemented up to the export section


3 files changed, 320 insertions(+), 192 deletions(-)

src/link/Wasm.zig+198-43
...@@ -404,7 +404,7 @@ pub const SymbolFlags = packed struct(u32) {...@@ -404,7 +404,7 @@ pub const SymbolFlags = packed struct(u32) {
404 /// Zig-specific. Data segments only.404 /// Zig-specific. Data segments only.
405 alignment: Alignment = .none,405 alignment: Alignment = .none,
406 /// Zig-specific. Globals only.406 /// Zig-specific. Globals only.
407 global_type: Global.Type = .zero,407 global_type: GlobalType4 = .zero,
408 /// Zig-specific. Tables only.408 /// Zig-specific. Tables only.
409 limits_has_max: bool = false,409 limits_has_max: bool = false,
410 /// Zig-specific. Tables only.410 /// Zig-specific. Tables only.
...@@ -481,6 +481,48 @@ pub const SymbolFlags = packed struct(u32) {...@@ -481,6 +481,48 @@ pub const SymbolFlags = packed struct(u32) {
481 }481 }
482};482};
483483
484pub const GlobalType4 = packed struct(u4) {
485 valtype: Valtype3,
486 mutable: bool,
487
488 pub const zero: GlobalType4 = @bitCast(@as(u4, 0));
489
490 pub fn to(gt: GlobalType4) Global.Type {
491 return .{
492 .valtype = gt.valtype.to(),
493 .mutable = gt.mutable,
494 };
495 }
496};
497
498pub const Valtype3 = enum(u3) {
499 i32,
500 i64,
501 f32,
502 f64,
503 v128,
504
505 pub fn from(v: std.wasm.Valtype) Valtype3 {
506 return switch (v) {
507 .i32 => .i32,
508 .i64 => .i64,
509 .f32 => .f32,
510 .f64 => .f64,
511 .v128 => .v128,
512 };
513 }
514
515 pub fn to(v: Valtype3) std.wasm.Valtype {
516 return switch (v) {
517 .i32 => .i32,
518 .i64 => .i64,
519 .f32 => .f32,
520 .f64 => .f64,
521 .v128 => .v128,
522 };
523 }
524};
525
484pub const NavObj = extern struct {526pub const NavObj = extern struct {
485 code: DataSegment.Payload,527 code: DataSegment.Payload,
486 /// Empty if not emitting an object.528 /// Empty if not emitting an object.
...@@ -591,7 +633,7 @@ pub const FunctionImport = extern struct {...@@ -591,7 +633,7 @@ pub const FunctionImport = extern struct {
591 __zig_error_names,633 __zig_error_names,
592 object_function: ObjectFunctionIndex,634 object_function: ObjectFunctionIndex,
593 nav_exe: NavExe.Index,635 nav_exe: NavExe.Index,
594 nav_obj: NavExe.Index,636 nav_obj: NavObj.Index,
595 };637 };
596638
597 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {639 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
...@@ -649,6 +691,20 @@ pub const FunctionImport = extern struct {...@@ -649,6 +691,20 @@ pub const FunctionImport = extern struct {
649 else => false,691 else => false,
650 };692 };
651 }693 }
694
695 pub fn typeIndex(r: Resolution, wasm: *const Wasm) FunctionType.Index {
696 return switch (unpack(r, wasm)) {
697 .unresolved => unreachable,
698 .__wasm_apply_global_tls_relocs => @panic("TODO"),
699 .__wasm_call_ctors => @panic("TODO"),
700 .__wasm_init_memory => @panic("TODO"),
701 .__wasm_init_tls => @panic("TODO"),
702 .__zig_error_names => @panic("TODO"),
703 .object_function => |i| i.ptr(wasm).type_index,
704 .nav_exe => @panic("TODO"),
705 .nav_obj => @panic("TODO"),
706 };
707 }
652 };708 };
653709
654 /// Index into `object_function_imports`.710 /// Index into `object_function_imports`.
...@@ -731,11 +787,13 @@ pub const GlobalImport = extern struct {...@@ -731,11 +787,13 @@ pub const GlobalImport = extern struct {
731 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {787 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
732 return switch (r) {788 return switch (r) {
733 .unresolved => .unresolved,789 .unresolved => .unresolved,
734 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,790 .__heap_base => .__heap_base,
735 .__wasm_call_ctors => .__wasm_call_ctors,791 .__heap_end => .__heap_end,
736 .__wasm_init_memory => .__wasm_init_memory,792 .__stack_pointer => .__stack_pointer,
737 .__wasm_init_tls => .__wasm_init_tls,793 .__tls_align => .__tls_align,
738 .__zig_error_names => .__zig_error_names,794 .__tls_base => .__tls_base,
795 .__tls_size => .__tls_size,
796 .__zig_error_name_table => .__zig_error_name_table,
739 _ => {797 _ => {
740 const i: u32 = @intFromEnum(r);798 const i: u32 = @intFromEnum(r);
741 const object_global_index = i - first_object_global;799 const object_global_index = i - first_object_global;
...@@ -780,12 +838,28 @@ pub const GlobalImport = extern struct {...@@ -780,12 +838,28 @@ pub const GlobalImport = extern struct {
780 }838 }
781 };839 };
782840
783 /// Index into `object_global_imports`.841 /// Index into `Wasm.object_global_imports`.
784 pub const Index = enum(u32) {842 pub const Index = enum(u32) {
785 _,843 _,
786844
787 pub fn ptr(index: Index, wasm: *const Wasm) *GlobalImport {845 pub fn key(index: Index, wasm: *const Wasm) *String {
788 return &wasm.object_global_imports.items[@intFromEnum(index)];846 return &wasm.object_global_imports.keys()[@intFromEnum(index)];
847 }
848
849 pub fn value(index: Index, wasm: *const Wasm) *GlobalImport {
850 return &wasm.object_global_imports.values()[@intFromEnum(index)];
851 }
852
853 pub fn name(index: Index, wasm: *const Wasm) String {
854 return index.key(wasm).*;
855 }
856
857 pub fn moduleName(index: Index, wasm: *const Wasm) String {
858 return index.value(wasm).module_name;
859 }
860
861 pub fn globalType(index: Index, wasm: *const Wasm) Global.Type {
862 return value(index, wasm).flags.global_type.to();
789 }863 }
790 };864 };
791};865};
...@@ -796,39 +870,9 @@ pub const Global = extern struct {...@@ -796,39 +870,9 @@ pub const Global = extern struct {
796 flags: SymbolFlags,870 flags: SymbolFlags,
797 expr: Expr,871 expr: Expr,
798872
799 pub const Type = packed struct(u4) {873 pub const Type = struct {
800 valtype: Valtype,874 valtype: std.wasm.Valtype,
801 mutable: bool,875 mutable: bool,
802
803 pub const zero: Type = @bitCast(@as(u4, 0));
804 };
805
806 pub const Valtype = enum(u3) {
807 i32,
808 i64,
809 f32,
810 f64,
811 v128,
812
813 pub fn from(v: std.wasm.Valtype) Valtype {
814 return switch (v) {
815 .i32 => .i32,
816 .i64 => .i64,
817 .f32 => .f32,
818 .f64 => .f64,
819 .v128 => .v128,
820 };
821 }
822
823 pub fn to(v: Valtype) std.wasm.Valtype {
824 return switch (v) {
825 .i32 => .i32,
826 .i64 => .i64,
827 .f32 => .f32,
828 .f64 => .f64,
829 .v128 => .v128,
830 };
831 }
832 };876 };
833};877};
834878
...@@ -865,6 +909,38 @@ pub const TableImport = extern struct {...@@ -865,6 +909,38 @@ pub const TableImport = extern struct {
865 __indirect_function_table,909 __indirect_function_table,
866 // Next, index into `object_tables`.910 // Next, index into `object_tables`.
867 _,911 _,
912
913 const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1;
914
915 pub const Unpacked = union(enum) {
916 unresolved,
917 __indirect_function_table,
918 object_table: ObjectTableIndex,
919 };
920
921 pub fn unpack(r: Resolution) Unpacked {
922 return switch (r) {
923 .unresolved => .unresolved,
924 .__indirect_function_table => .__indirect_function_table,
925 _ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) },
926 };
927 }
928
929 pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
930 return switch (unpack(r)) {
931 .unresolved => unreachable,
932 .__indirect_function_table => @panic("TODO"),
933 .object_table => |i| i.ptr(wasm).flags.ref_type.to(),
934 };
935 }
936
937 pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits {
938 return switch (unpack(r)) {
939 .unresolved => unreachable,
940 .__indirect_function_table => @panic("TODO"),
941 .object_table => |i| i.ptr(wasm).limits(),
942 };
943 }
868 };944 };
869945
870 /// Index into `object_table_imports`.946 /// Index into `object_table_imports`.
...@@ -878,7 +954,26 @@ pub const TableImport = extern struct {...@@ -878,7 +954,26 @@ pub const TableImport = extern struct {
878 pub fn value(index: Index, wasm: *const Wasm) *TableImport {954 pub fn value(index: Index, wasm: *const Wasm) *TableImport {
879 return &wasm.object_table_imports.values()[@intFromEnum(index)];955 return &wasm.object_table_imports.values()[@intFromEnum(index)];
880 }956 }
957
958 pub fn name(index: Index, wasm: *const Wasm) String {
959 return index.key(wasm).*;
960 }
961
962 pub fn moduleName(index: Index, wasm: *const Wasm) String {
963 return index.value(wasm).module_name;
964 }
881 };965 };
966
967 pub fn limits(ti: *const TableImport) std.wasm.Limits {
968 return .{
969 .flags = .{
970 .has_max = ti.flags.limits_has_max,
971 .is_shared = ti.flags.limits_is_shared,
972 },
973 .min = ti.limits_min,
974 .max = ti.limits_max,
975 };
976 }
882};977};
883978
884pub const Table = extern struct {979pub const Table = extern struct {
...@@ -887,6 +982,17 @@ pub const Table = extern struct {...@@ -887,6 +982,17 @@ pub const Table = extern struct {
887 flags: SymbolFlags,982 flags: SymbolFlags,
888 limits_min: u32,983 limits_min: u32,
889 limits_max: u32,984 limits_max: u32,
985
986 pub fn limits(t: *const Table) std.wasm.Limits {
987 return .{
988 .flags = .{
989 .has_max = t.flags.limits_has_max,
990 .is_shared = t.flags.limits_is_shared,
991 },
992 .min = t.limits_min,
993 .max = t.limits_max,
994 };
995 }
890};996};
891997
892/// Uniquely identifies a section across all objects. By subtracting998/// Uniquely identifies a section across all objects. By subtracting
...@@ -910,9 +1016,13 @@ pub const GlobalImportIndex = enum(u32) {...@@ -910,9 +1016,13 @@ pub const GlobalImportIndex = enum(u32) {
910 _,1016 _,
911};1017};
9121018
913/// Index into `object_globals`.1019/// Index into `Wasm.object_globals`.
914pub const ObjectGlobalIndex = enum(u32) {1020pub const ObjectGlobalIndex = enum(u32) {
915 _,1021 _,
1022
1023 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
1024 return &wasm.object_globals.items[@intFromEnum(index)];
1025 }
916};1026};
9171027
918/// Index into `Wasm.object_memories`.1028/// Index into `Wasm.object_memories`.
...@@ -992,6 +1102,16 @@ pub const CustomSegment = extern struct {...@@ -992,6 +1102,16 @@ pub const CustomSegment = extern struct {
992/// An index into string_bytes where a wasm expression is found.1102/// An index into string_bytes where a wasm expression is found.
993pub const Expr = enum(u32) {1103pub const Expr = enum(u32) {
994 _,1104 _,
1105
1106 pub const end = @intFromEnum(std.wasm.Opcode.end);
1107
1108 pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {
1109 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
1110 const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) {
1111 error.InvalidInitOpcode => unreachable,
1112 };
1113 return start_slice[0..end_pos :end];
1114 }
995};1115};
9961116
997pub const FunctionType = extern struct {1117pub const FunctionType = extern struct {
...@@ -1162,6 +1282,12 @@ pub const ZcuImportIndex = enum(u32) {...@@ -1162,6 +1282,12 @@ pub const ZcuImportIndex = enum(u32) {
1162 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;1282 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
1163 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;1283 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
1164 }1284 }
1285
1286 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) Global.Type {
1287 _ = index;
1288 _ = wasm;
1289 unreachable; // Zig has no way to create Wasm globals yet.
1290 }
1165};1291};
11661292
1167/// 0. Index into `object_function_imports`.1293/// 0. Index into `object_function_imports`.
...@@ -1274,6 +1400,24 @@ pub const GlobalImportId = enum(u32) {...@@ -1274,6 +1400,24 @@ pub const GlobalImportId = enum(u32) {
1274 .zcu_import => return .zig_object_nofile, // TODO give a better source location1400 .zcu_import => return .zig_object_nofile, // TODO give a better source location
1275 }1401 }
1276 }1402 }
1403
1404 pub fn name(id: GlobalImportId, wasm: *const Wasm) String {
1405 return switch (unpack(id, wasm)) {
1406 inline .object_global_import, .zcu_import => |i| i.name(wasm),
1407 };
1408 }
1409
1410 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) String {
1411 return switch (unpack(id, wasm)) {
1412 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
1413 };
1414 }
1415
1416 pub fn globalType(id: GlobalImportId, wasm: *Wasm) Global.Type {
1417 return switch (unpack(id, wasm)) {
1418 inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
1419 };
1420 }
1277};1421};
12781422
1279/// Index into `Wasm.symbol_table`.1423/// Index into `Wasm.symbol_table`.
...@@ -1384,6 +1528,17 @@ pub const MemoryImport = extern struct {...@@ -1384,6 +1528,17 @@ pub const MemoryImport = extern struct {
1384 limits_has_max: bool,1528 limits_has_max: bool,
1385 limits_is_shared: bool,1529 limits_is_shared: bool,
1386 padding: [2]u8 = .{ 0, 0 },1530 padding: [2]u8 = .{ 0, 0 },
1531
1532 pub fn limits(mi: *const MemoryImport) std.wasm.Limits {
1533 return .{
1534 .flags = .{
1535 .has_max = mi.limits_has_max,
1536 .is_shared = mi.limits_is_shared,
1537 },
1538 .min = mi.limits_min,
1539 .max = mi.limits_max,
1540 };
1541 }
1387};1542};
13881543
1389pub const Alignment = InternPool.Alignment;1544pub const Alignment = InternPool.Alignment;
src/link/Wasm/Flush.zig+116-147
...@@ -335,8 +335,6 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -335,8 +335,6 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
335 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});335 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
336 }336 }
337337
338 // Size of each section header
339 const header_size = 5 + 1;
340 var section_index: u32 = 0;338 var section_index: u32 = 0;
341 // Index of the code section. Used to tell relocation table where the section lives.339 // Index of the code section. Used to tell relocation table where the section lives.
342 var code_section_index: ?u32 = null;340 var code_section_index: ?u32 = null;
...@@ -369,54 +367,50 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -369,54 +367,50 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
369 }367 }
370 }368 }
371369
372 try writeVecSectionHeader(370 replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(wasm.func_types.entries.len));
373 binary_bytes.items,
374 header_offset,
375 .type,
376 @intCast(binary_bytes.items.len - header_offset - header_size),
377 @intCast(wasm.func_types.entries.len),
378 );
379 section_index += 1;371 section_index += 1;
380 }372 }
381373
382 // Import section374 // Import section
383 const total_imports_len = wasm.function_imports.entries.len + wasm.global_imports.entries.len +375 {
384 wasm.table_imports.entries.len + wasm.object_memory_imports.items.len + @intFromBool(import_memory);376 var total_imports: usize = 0;
385
386 if (total_imports_len > 0) {
387 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);377 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
388378
389 for (wasm.function_imports.values()) |*function_import| {379 for (wasm.function_imports.values()) |id| {
390 const module_name = function_import.moduleName(wasm).slice(wasm);380 const module_name = id.moduleName(wasm).slice(wasm);
391 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));381 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
392 try binary_writer.writeAll(module_name);382 try binary_writer.writeAll(module_name);
393383
394 const name = function_import.name(wasm).slice(wasm);384 const name = id.name(wasm).slice(wasm);
395 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));385 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
396 try binary_writer.writeAll(name);386 try binary_writer.writeAll(name);
397387
398 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));388 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
399 try leb.writeUleb128(binary_writer, @intFromEnum(function_import.functionType(wasm)));389 try leb.writeUleb128(binary_writer, @intFromEnum(id.functionType(wasm)));
400 }390 }
391 total_imports += wasm.function_imports.entries.len;
401392
402 for (wasm.table_imports.values()) |*table_import| {393 for (wasm.table_imports.values()) |id| {
403 const module_name = table_import.moduleName(wasm).slice(wasm);394 const table_import = id.value(wasm);
395 const module_name = table_import.module_name.slice(wasm);
404 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));396 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
405 try binary_writer.writeAll(module_name);397 try binary_writer.writeAll(module_name);
406398
407 const name = table_import.name(wasm).slice(wasm);399 const name = id.key(wasm).slice(wasm);
408 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));400 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
409 try binary_writer.writeAll(name);401 try binary_writer.writeAll(name);
410402
411 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));403 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
412 try leb.writeUleb128(binary_writer, std.wasm.reftype(table_import.reftype));404 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
413 try emitLimits(binary_writer, table_import.limits);405 try emitLimits(gpa, binary_bytes, table_import.limits());
414 }406 }
407 total_imports += wasm.table_imports.entries.len;
415408
416 for (wasm.object_memory_imports.items) |*memory_import| {409 for (wasm.object_memory_imports.items) |*memory_import| {
417 try emitMemoryImport(wasm, binary_writer, memory_import);410 try emitMemoryImport(wasm, binary_bytes, memory_import);
411 total_imports += 1;
418 } else if (import_memory) {412 } else if (import_memory) {
419 try emitMemoryImport(wasm, binary_writer, &.{413 try emitMemoryImport(wasm, binary_bytes, &.{
420 .module_name = wasm.host_name,414 .module_name = wasm.host_name,
421 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,415 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
422 .limits_min = wasm.memories.limits.min,416 .limits_min = wasm.memories.limits.min,
...@@ -424,100 +418,87 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -424,100 +418,87 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
424 .limits_has_max = wasm.memories.limits.flags.has_max,418 .limits_has_max = wasm.memories.limits.flags.has_max,
425 .limits_is_shared = wasm.memories.limits.flags.is_shared,419 .limits_is_shared = wasm.memories.limits.flags.is_shared,
426 });420 });
421 total_imports += 1;
427 }422 }
428423
429 for (wasm.global_imports.values()) |*global_import| {424 for (wasm.global_imports.values()) |id| {
430 const module_name = global_import.module_name.slice(wasm);425 const module_name = id.moduleName(wasm).slice(wasm);
431 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));426 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
432 try binary_writer.writeAll(module_name);427 try binary_writer.writeAll(module_name);
433428
434 const name = global_import.name.slice(wasm);429 const name = id.name(wasm).slice(wasm);
435 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));430 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
436 try binary_writer.writeAll(name);431 try binary_writer.writeAll(name);
437432
438 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));433 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
439 try leb.writeUleb128(binary_writer, @intFromEnum(global_import.valtype));434 const global_type = id.globalType(wasm);
440 try binary_writer.writeByte(@intFromBool(global_import.mutable));435 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
436 try binary_writer.writeByte(@intFromBool(global_type.mutable));
441 }437 }
438 total_imports += wasm.global_imports.entries.len;
442439
443 try writeVecSectionHeader(440 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));
444 binary_bytes.items,
445 header_offset,
446 .import,
447 @intCast(binary_bytes.items.len - header_offset - header_size),
448 @intCast(total_imports_len),
449 );
450 section_index += 1;441 section_index += 1;
451 }442 }
452443
453 // Function section444 // Function section
454 if (wasm.functions.count() != 0) {445 if (wasm.functions.count() != 0) {
455 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);446 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
456 for (wasm.functions.values()) |function| {447 for (wasm.functions.keys()) |function| {
457 try leb.writeUleb128(binary_writer, function.func.type_index);448 try leb.writeUleb128(binary_writer, @intFromEnum(function.typeIndex(wasm)));
458 }449 }
459450
460 try writeVecSectionHeader(451 replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count()));
461 binary_bytes.items,
462 header_offset,
463 .function,
464 @intCast(binary_bytes.items.len - header_offset - header_size),
465 @intCast(wasm.functions.count()),
466 );
467 section_index += 1;452 section_index += 1;
468 }453 }
469454
470 // Table section455 // Table section
471 if (wasm.tables.items.len > 0) {456 if (wasm.tables.entries.len > 0) {
472 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);457 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
473458
474 for (wasm.tables.items) |table| {459 for (wasm.tables.keys()) |table| {
475 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));460 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm))));
476 try emitLimits(binary_writer, table.limits);461 try emitLimits(gpa, binary_bytes, table.limits(wasm));
477 }462 }
478463
479 try writeVecSectionHeader(464 replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len));
480 binary_bytes.items,
481 header_offset,
482 .table,
483 @intCast(binary_bytes.items.len - header_offset - header_size),
484 @intCast(wasm.tables.items.len),
485 );
486 section_index += 1;465 section_index += 1;
487 }466 }
488467
489 // Memory section468 // Memory section. wasm currently only supports 1 linear memory segment.
490 if (!import_memory) {469 if (!import_memory) {
491 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);470 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
492471 try emitLimits(gpa, binary_bytes, wasm.memories.limits);
493 try emitLimits(binary_writer, wasm.memories.limits);472 replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1);
494 try writeVecSectionHeader(
495 binary_bytes.items,
496 header_offset,
497 .memory,
498 @intCast(binary_bytes.items.len - header_offset - header_size),
499 1, // wasm currently only supports 1 linear memory segment
500 );
501 section_index += 1;473 section_index += 1;
502 }474 }
503475
504 // Global section (used to emit stack pointer)476 // Global section (used to emit stack pointer)
505 if (wasm.output_globals.items.len > 0) {477 if (wasm.globals.entries.len > 0) {
506 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);478 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
507479
508 for (wasm.output_globals.items) |global| {480 for (wasm.globals.keys()) |global_resolution| {
509 try binary_writer.writeByte(@intFromEnum(global.global_type.valtype));481 switch (global_resolution.unpack(wasm)) {
510 try binary_writer.writeByte(@intFromBool(global.global_type.mutable));482 .unresolved => unreachable,
511 try emitInit(binary_writer, global.init);483 .__heap_base => @panic("TODO"),
484 .__heap_end => @panic("TODO"),
485 .__stack_pointer => @panic("TODO"),
486 .__tls_align => @panic("TODO"),
487 .__tls_base => @panic("TODO"),
488 .__tls_size => @panic("TODO"),
489 .__zig_error_name_table => @panic("TODO"),
490 .object_global => |i| {
491 const global = i.ptr(wasm);
492 try binary_writer.writeByte(@intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())));
493 try binary_writer.writeByte(@intFromBool(global.flags.global_type.mutable));
494 try emitExpr(wasm, binary_bytes, global.expr);
495 },
496 .nav_exe => @panic("TODO"),
497 .nav_obj => @panic("TODO"),
498 }
512 }499 }
513500
514 try writeVecSectionHeader(501 replaceVecSectionHeader(binary_bytes, header_offset, .global, @intCast(wasm.globals.entries.len));
515 binary_bytes.items,
516 header_offset,
517 .global,
518 @intCast(binary_bytes.items.len - header_offset - header_size),
519 @intCast(wasm.output_globals.items.len),
520 );
521 section_index += 1;502 section_index += 1;
522 }503 }
523504
...@@ -540,25 +521,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -540,25 +521,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
540 try leb.writeUleb128(binary_writer, @as(u32, 0));521 try leb.writeUleb128(binary_writer, @as(u32, 0));
541 }522 }
542523
543 try writeVecSectionHeader(524 const n_items: u32 = @intCast(wasm.exports.items.len + @intFromBool(export_memory));
544 binary_bytes.items,525 replaceVecSectionHeader(binary_bytes, header_offset, .@"export", n_items);
545 header_offset,
546 .@"export",
547 @intCast(binary_bytes.items.len - header_offset - header_size),
548 @intCast(wasm.exports.items.len + @intFromBool(export_memory)),
549 );
550 section_index += 1;526 section_index += 1;
551 }527 }
552528
553 if (wasm.entry) |entry_index| {529 if (Wasm.FunctionIndex.fromResolution(wasm.entry_resolution)) |entry_index| {
554 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);530 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
555 try writeVecSectionHeader(531 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));
556 binary_bytes.items,
557 header_offset,
558 .start,
559 @intCast(binary_bytes.items.len - header_offset - header_size),
560 entry_index,
561 );
562 }532 }
563533
564 // element section (function table)534 // element section (function table)
...@@ -586,26 +556,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -586,26 +556,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
586 try leb.writeUleb128(binary_writer, sym.index);556 try leb.writeUleb128(binary_writer, sym.index);
587 }557 }
588558
589 try writeVecSectionHeader(559 replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
590 binary_bytes.items,
591 header_offset,
592 .element,
593 @intCast(binary_bytes.items.len - header_offset - header_size),
594 1,
595 );
596 section_index += 1;560 section_index += 1;
597 }561 }
598562
599 // When the shared-memory option is enabled, we *must* emit the 'data count' section.563 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
600 if (f.data_segment_groups.items.len > 0 and shared_memory) {564 if (f.data_segment_groups.items.len > 0 and shared_memory) {
601 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);565 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
602 try writeVecSectionHeader(566 replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));
603 binary_bytes.items,
604 header_offset,
605 .data_count,
606 @intCast(binary_bytes.items.len - header_offset - header_size),
607 @intCast(f.data_segment_groups.items.len),
608 );
609 }567 }
610568
611 // Code section.569 // Code section.
...@@ -636,13 +594,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -636,13 +594,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
636 },594 },
637 };595 };
638596
639 try writeVecSectionHeader(597 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
640 binary_bytes.items,
641 header_offset,
642 .code,
643 @intCast(binary_bytes.items.len - header_offset - header_size),
644 @intCast(wasm.functions.count()),
645 );
646 code_section_index = section_index;598 code_section_index = section_index;
647 section_index += 1;599 section_index += 1;
648 }600 }
...@@ -682,13 +634,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {...@@ -682,13 +634,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
682 }634 }
683 assert(group_index == f.data_segment_groups.items.len);635 assert(group_index == f.data_segment_groups.items.len);
684636
685 try writeVecSectionHeader(637 replaceVecSectionHeader(binary_bytes, header_offset, .data, group_index);
686 binary_bytes.items,
687 header_offset,
688 .data,
689 @intCast(binary_bytes.items.len - header_offset - header_size),
690 group_index,
691 );
692 data_section_index = section_index;638 data_section_index = section_index;
693 section_index += 1;639 section_index += 1;
694 }640 }
...@@ -768,7 +714,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena...@@ -768,7 +714,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena
768 };714 };
769715
770 var globals: std.MultiArrayList(NamedIndex) = .empty;716 var globals: std.MultiArrayList(NamedIndex) = .empty;
771 try globals.ensureTotalCapacityPrecise(arena, wasm.output_globals.items.len + wasm.global_imports.items.len);717 try globals.ensureTotalCapacityPrecise(arena, wasm.globals.items.len + wasm.global_imports.items.len);
772718
773 var segments: std.MultiArrayList(NamedIndex) = .empty;719 var segments: std.MultiArrayList(NamedIndex) = .empty;
774 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());720 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());
...@@ -844,10 +790,8 @@ fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {...@@ -844,10 +790,8 @@ fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
844}790}
845791
846fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {792fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
847 // unlike regular section, we don't emit the count793 try bytes.appendNTimes(gpa, 0, section_header_size);
848 const header_size = 1 + 5;794 return @intCast(bytes.items.len - section_header_size);
849 try bytes.appendNTimes(gpa, 0, header_size);
850 return @intCast(bytes.items.len - header_size);
851}795}
852796
853fn emitNameSubsection(797fn emitNameSubsection(
...@@ -1106,38 +1050,57 @@ fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index:...@@ -1106,38 +1050,57 @@ fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index:
1106 return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);1050 return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);
1107}1051}
11081052
1053/// section id + fixed leb contents size + fixed leb vector length
1054const section_header_reserve_size = 1 + 5 + 5;
1055const section_header_size = 5 + 1;
1056
1109fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {1057fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1110 // section id + fixed leb contents size + fixed leb vector length1058 try bytes.appendNTimes(gpa, 0, section_header_reserve_size);
1111 const header_size = 1 + 5 + 5;1059 return @intCast(bytes.items.len - section_header_reserve_size);
1112 try bytes.appendNTimes(gpa, 0, header_size);
1113 return @intCast(bytes.items.len - header_size);
1114}1060}
11151061
1116fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {1062fn replaceVecSectionHeader(
1117 var buf: [1 + 5 + 5]u8 = undefined;1063 bytes: *std.ArrayListUnmanaged(u8),
1118 buf[0] = @intFromEnum(section);1064 offset: u32,
1119 leb.writeUnsignedFixed(5, buf[1..6], size);1065 section: std.wasm.Section,
1120 leb.writeUnsignedFixed(5, buf[6..], items);1066 n_items: u32,
1121 buffer[offset..][0..buf.len].* = buf;1067) void {
1068 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
1069 var buf: [section_header_reserve_size]u8 = undefined;
1070 var fbw = std.io.fixedBufferStream(&buf);
1071 const w = fbw.writer();
1072 w.writeByte(@intFromEnum(section)) catch unreachable;
1073 leb.writeUleb128(w, size) catch unreachable;
1074 leb.writeUleb128(w, n_items) catch unreachable;
1075 bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());
1122}1076}
11231077
1124fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {1078fn emitLimits(
1125 try writer.writeByte(limits.flags);1079 gpa: Allocator,
1126 try leb.writeUleb128(writer, limits.min);1080 binary_bytes: *std.ArrayListUnmanaged(u8),
1127 if (limits.flags.has_max) try leb.writeUleb128(writer, limits.max);1081 limits: std.wasm.Limits,
1082) Allocator.Error!void {
1083 try binary_bytes.append(gpa, @bitCast(limits.flags));
1084 try leb.writeUleb128(binary_bytes.writer(gpa), limits.min);
1085 if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max);
1128}1086}
11291087
1130fn emitMemoryImport(wasm: *Wasm, writer: anytype, memory_import: *const Wasm.MemoryImport) Allocator.Error!void {1088fn emitMemoryImport(
1089 wasm: *Wasm,
1090 binary_bytes: *std.ArrayListUnmanaged(u8),
1091 memory_import: *const Wasm.MemoryImport,
1092) Allocator.Error!void {
1093 const gpa = wasm.base.comp.gpa;
1131 const module_name = memory_import.module_name.slice(wasm);1094 const module_name = memory_import.module_name.slice(wasm);
1132 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));1095 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len)));
1133 try writer.writeAll(module_name);1096 try binary_bytes.appendSlice(gpa, module_name);
11341097
1135 const name = memory_import.name.slice(wasm);1098 const name = memory_import.name.slice(wasm);
1136 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));1099 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1137 try writer.writeAll(name);1100 try binary_bytes.appendSlice(gpa, name);
11381101
1139 try writer.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));1102 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
1140 try emitLimits(writer, memory_import.limits());1103 try emitLimits(gpa, binary_bytes, memory_import.limits());
1141}1104}
11421105
1143pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {1106pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
...@@ -1166,6 +1129,12 @@ pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {...@@ -1166,6 +1129,12 @@ pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
1166 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));1129 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));
1167}1130}
11681131
1132pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void {
1133 const gpa = wasm.base.comp.gpa;
1134 const slice = expr.slice(wasm);
1135 try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode
1136}
1137
1169//fn emitLinkSection(1138//fn emitLinkSection(
1170// wasm: *Wasm,1139// wasm: *Wasm,
1171// binary_bytes: *std.ArrayListUnmanaged(u8),1140// binary_bytes: *std.ArrayListUnmanaged(u8),
src/link/Wasm/Object.zig+6-2
...@@ -1040,9 +1040,9 @@ fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usi...@@ -1040,9 +1040,9 @@ fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usi
1040 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };1040 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
1041}1041}
10421042
1043fn skipInit(bytes: []const u8, pos: usize) !usize {1043pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {
1044 const opcode = bytes[pos];1044 const opcode = bytes[pos];
1045 const end_pos = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {1045 return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
1046 .i32_const => readLeb(i32, bytes, pos + 1)[1],1046 .i32_const => readLeb(i32, bytes, pos + 1)[1],
1047 .i64_const => readLeb(i64, bytes, pos + 1)[1],1047 .i64_const => readLeb(i64, bytes, pos + 1)[1],
1048 .f32_const => pos + 5,1048 .f32_const => pos + 5,
...@@ -1050,6 +1050,10 @@ fn skipInit(bytes: []const u8, pos: usize) !usize {...@@ -1050,6 +1050,10 @@ fn skipInit(bytes: []const u8, pos: usize) !usize {
1050 .global_get => readLeb(u32, bytes, pos + 1)[1],1050 .global_get => readLeb(u32, bytes, pos + 1)[1],
1051 else => return error.InvalidInitOpcode,1051 else => return error.InvalidInitOpcode,
1052 };1052 };
1053}
1054
1055fn skipInit(bytes: []const u8, pos: usize) !usize {
1056 const end_pos = try exprEndPos(bytes, pos);
1053 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);1057 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
1054 if (op != .end) return error.InitExprMissingEnd;1058 if (op != .end) return error.InitExprMissingEnd;
1055 return final_pos;1059 return final_pos;