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) {
404404 /// Zig-specific. Data segments only.
405405 alignment: Alignment = .none,
406406 /// Zig-specific. Globals only.
407 global_type: Global.Type = .zero,
407 global_type: GlobalType4 = .zero,
408408 /// Zig-specific. Tables only.
409409 limits_has_max: bool = false,
410410 /// Zig-specific. Tables only.
......@@ -481,6 +481,48 @@ pub const SymbolFlags = packed struct(u32) {
481481 }
482482};
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
484526pub const NavObj = extern struct {
485527 code: DataSegment.Payload,
486528 /// Empty if not emitting an object.
......@@ -591,7 +633,7 @@ pub const FunctionImport = extern struct {
591633 __zig_error_names,
592634 object_function: ObjectFunctionIndex,
593635 nav_exe: NavExe.Index,
594 nav_obj: NavExe.Index,
636 nav_obj: NavObj.Index,
595637 };
596638
597639 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
......@@ -649,6 +691,20 @@ pub const FunctionImport = extern struct {
649691 else => false,
650692 };
651693 }
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 }
652708 };
653709
654710 /// Index into `object_function_imports`.
......@@ -731,11 +787,13 @@ pub const GlobalImport = extern struct {
731787 pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
732788 return switch (r) {
733789 .unresolved => .unresolved,
734 .__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
735 .__wasm_call_ctors => .__wasm_call_ctors,
736 .__wasm_init_memory => .__wasm_init_memory,
737 .__wasm_init_tls => .__wasm_init_tls,
738 .__zig_error_names => .__zig_error_names,
790 .__heap_base => .__heap_base,
791 .__heap_end => .__heap_end,
792 .__stack_pointer => .__stack_pointer,
793 .__tls_align => .__tls_align,
794 .__tls_base => .__tls_base,
795 .__tls_size => .__tls_size,
796 .__zig_error_name_table => .__zig_error_name_table,
739797 _ => {
740798 const i: u32 = @intFromEnum(r);
741799 const object_global_index = i - first_object_global;
......@@ -780,12 +838,28 @@ pub const GlobalImport = extern struct {
780838 }
781839 };
782840
783 /// Index into `object_global_imports`.
841 /// Index into `Wasm.object_global_imports`.
784842 pub const Index = enum(u32) {
785843 _,
786844
787 pub fn ptr(index: Index, wasm: *const Wasm) *GlobalImport {
788 return &wasm.object_global_imports.items[@intFromEnum(index)];
845 pub fn key(index: Index, wasm: *const Wasm) *String {
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();
789863 }
790864 };
791865};
......@@ -796,39 +870,9 @@ pub const Global = extern struct {
796870 flags: SymbolFlags,
797871 expr: Expr,
798872
799 pub const Type = packed struct(u4) {
800 valtype: Valtype,
873 pub const Type = struct {
874 valtype: std.wasm.Valtype,
801875 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 }
832876 };
833877};
834878
......@@ -865,6 +909,38 @@ pub const TableImport = extern struct {
865909 __indirect_function_table,
866910 // Next, index into `object_tables`.
867911 _,
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 }
868944 };
869945
870946 /// Index into `object_table_imports`.
......@@ -878,7 +954,26 @@ pub const TableImport = extern struct {
878954 pub fn value(index: Index, wasm: *const Wasm) *TableImport {
879955 return &wasm.object_table_imports.values()[@intFromEnum(index)];
880956 }
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 }
881965 };
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 }
882977};
883978
884979pub const Table = extern struct {
......@@ -887,6 +982,17 @@ pub const Table = extern struct {
887982 flags: SymbolFlags,
888983 limits_min: u32,
889984 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 }
890996};
891997
892998/// Uniquely identifies a section across all objects. By subtracting
......@@ -910,9 +1016,13 @@ pub const GlobalImportIndex = enum(u32) {
9101016 _,
9111017};
9121018
913/// Index into `object_globals`.
1019/// Index into `Wasm.object_globals`.
9141020pub const ObjectGlobalIndex = enum(u32) {
9151021 _,
1022
1023 pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
1024 return &wasm.object_globals.items[@intFromEnum(index)];
1025 }
9161026};
9171027
9181028/// Index into `Wasm.object_memories`.
......@@ -992,6 +1102,16 @@ pub const CustomSegment = extern struct {
9921102/// An index into string_bytes where a wasm expression is found.
9931103pub const Expr = enum(u32) {
9941104 _,
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 }
9951115};
9961116
9971117pub const FunctionType = extern struct {
......@@ -1162,6 +1282,12 @@ pub const ZcuImportIndex = enum(u32) {
11621282 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
11631283 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
11641284 }
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 }
11651291};
11661292
11671293/// 0. Index into `object_function_imports`.
......@@ -1274,6 +1400,24 @@ pub const GlobalImportId = enum(u32) {
12741400 .zcu_import => return .zig_object_nofile, // TODO give a better source location
12751401 }
12761402 }
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 }
12771421};
12781422
12791423/// Index into `Wasm.symbol_table`.
......@@ -1384,6 +1528,17 @@ pub const MemoryImport = extern struct {
13841528 limits_has_max: bool,
13851529 limits_is_shared: bool,
13861530 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 }
13871542};
13881543
13891544pub 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 {
335335 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
336336 }
337337
338 // Size of each section header
339 const header_size = 5 + 1;
340338 var section_index: u32 = 0;
341339 // Index of the code section. Used to tell relocation table where the section lives.
342340 var code_section_index: ?u32 = null;
......@@ -369,54 +367,50 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
369367 }
370368 }
371369
372 try writeVecSectionHeader(
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 );
370 replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(wasm.func_types.entries.len));
379371 section_index += 1;
380372 }
381373
382374 // Import section
383 const total_imports_len = wasm.function_imports.entries.len + wasm.global_imports.entries.len +
384 wasm.table_imports.entries.len + wasm.object_memory_imports.items.len + @intFromBool(import_memory);
385
386 if (total_imports_len > 0) {
375 {
376 var total_imports: usize = 0;
387377 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
388378
389 for (wasm.function_imports.values()) |*function_import| {
390 const module_name = function_import.moduleName(wasm).slice(wasm);
379 for (wasm.function_imports.values()) |id| {
380 const module_name = id.moduleName(wasm).slice(wasm);
391381 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
392382 try binary_writer.writeAll(module_name);
393383
394 const name = function_import.name(wasm).slice(wasm);
384 const name = id.name(wasm).slice(wasm);
395385 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
396386 try binary_writer.writeAll(name);
397387
398388 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)));
400390 }
391 total_imports += wasm.function_imports.entries.len;
401392
402 for (wasm.table_imports.values()) |*table_import| {
403 const module_name = table_import.moduleName(wasm).slice(wasm);
393 for (wasm.table_imports.values()) |id| {
394 const table_import = id.value(wasm);
395 const module_name = table_import.module_name.slice(wasm);
404396 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
405397 try binary_writer.writeAll(module_name);
406398
407 const name = table_import.name(wasm).slice(wasm);
399 const name = id.key(wasm).slice(wasm);
408400 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
409401 try binary_writer.writeAll(name);
410402
411403 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
412 try leb.writeUleb128(binary_writer, std.wasm.reftype(table_import.reftype));
413 try emitLimits(binary_writer, table_import.limits);
404 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
405 try emitLimits(gpa, binary_bytes, table_import.limits());
414406 }
407 total_imports += wasm.table_imports.entries.len;
415408
416409 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;
418412 } else if (import_memory) {
419 try emitMemoryImport(wasm, binary_writer, &.{
413 try emitMemoryImport(wasm, binary_bytes, &.{
420414 .module_name = wasm.host_name,
421415 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
422416 .limits_min = wasm.memories.limits.min,
......@@ -424,100 +418,87 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
424418 .limits_has_max = wasm.memories.limits.flags.has_max,
425419 .limits_is_shared = wasm.memories.limits.flags.is_shared,
426420 });
421 total_imports += 1;
427422 }
428423
429 for (wasm.global_imports.values()) |*global_import| {
430 const module_name = global_import.module_name.slice(wasm);
424 for (wasm.global_imports.values()) |id| {
425 const module_name = id.moduleName(wasm).slice(wasm);
431426 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
432427 try binary_writer.writeAll(module_name);
433428
434 const name = global_import.name.slice(wasm);
429 const name = id.name(wasm).slice(wasm);
435430 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
436431 try binary_writer.writeAll(name);
437432
438433 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
439 try leb.writeUleb128(binary_writer, @intFromEnum(global_import.valtype));
440 try binary_writer.writeByte(@intFromBool(global_import.mutable));
434 const global_type = id.globalType(wasm);
435 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
436 try binary_writer.writeByte(@intFromBool(global_type.mutable));
441437 }
438 total_imports += wasm.global_imports.entries.len;
442439
443 try writeVecSectionHeader(
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 );
440 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));
450441 section_index += 1;
451442 }
452443
453444 // Function section
454445 if (wasm.functions.count() != 0) {
455446 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
456 for (wasm.functions.values()) |function| {
457 try leb.writeUleb128(binary_writer, function.func.type_index);
447 for (wasm.functions.keys()) |function| {
448 try leb.writeUleb128(binary_writer, @intFromEnum(function.typeIndex(wasm)));
458449 }
459450
460 try writeVecSectionHeader(
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 );
451 replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count()));
467452 section_index += 1;
468453 }
469454
470455 // Table section
471 if (wasm.tables.items.len > 0) {
456 if (wasm.tables.entries.len > 0) {
472457 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
473458
474 for (wasm.tables.items) |table| {
475 try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
476 try emitLimits(binary_writer, table.limits);
459 for (wasm.tables.keys()) |table| {
460 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm))));
461 try emitLimits(gpa, binary_bytes, table.limits(wasm));
477462 }
478463
479 try writeVecSectionHeader(
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 );
464 replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len));
486465 section_index += 1;
487466 }
488467
489 // Memory section
468 // Memory section. wasm currently only supports 1 linear memory segment.
490469 if (!import_memory) {
491470 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
492
493 try emitLimits(binary_writer, wasm.memories.limits);
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 );
471 try emitLimits(gpa, binary_bytes, wasm.memories.limits);
472 replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1);
501473 section_index += 1;
502474 }
503475
504476 // Global section (used to emit stack pointer)
505 if (wasm.output_globals.items.len > 0) {
477 if (wasm.globals.entries.len > 0) {
506478 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
507479
508 for (wasm.output_globals.items) |global| {
509 try binary_writer.writeByte(@intFromEnum(global.global_type.valtype));
510 try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
511 try emitInit(binary_writer, global.init);
480 for (wasm.globals.keys()) |global_resolution| {
481 switch (global_resolution.unpack(wasm)) {
482 .unresolved => unreachable,
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 }
512499 }
513500
514 try writeVecSectionHeader(
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 );
501 replaceVecSectionHeader(binary_bytes, header_offset, .global, @intCast(wasm.globals.entries.len));
521502 section_index += 1;
522503 }
523504
......@@ -540,25 +521,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
540521 try leb.writeUleb128(binary_writer, @as(u32, 0));
541522 }
542523
543 try writeVecSectionHeader(
544 binary_bytes.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 );
524 const n_items: u32 = @intCast(wasm.exports.items.len + @intFromBool(export_memory));
525 replaceVecSectionHeader(binary_bytes, header_offset, .@"export", n_items);
550526 section_index += 1;
551527 }
552528
553 if (wasm.entry) |entry_index| {
529 if (Wasm.FunctionIndex.fromResolution(wasm.entry_resolution)) |entry_index| {
554530 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
555 try writeVecSectionHeader(
556 binary_bytes.items,
557 header_offset,
558 .start,
559 @intCast(binary_bytes.items.len - header_offset - header_size),
560 entry_index,
561 );
531 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));
562532 }
563533
564534 // element section (function table)
......@@ -586,26 +556,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
586556 try leb.writeUleb128(binary_writer, sym.index);
587557 }
588558
589 try writeVecSectionHeader(
590 binary_bytes.items,
591 header_offset,
592 .element,
593 @intCast(binary_bytes.items.len - header_offset - header_size),
594 1,
595 );
559 replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
596560 section_index += 1;
597561 }
598562
599563 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
600564 if (f.data_segment_groups.items.len > 0 and shared_memory) {
601565 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
602 try writeVecSectionHeader(
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 );
566 replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));
609567 }
610568
611569 // Code section.
......@@ -636,13 +594,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
636594 },
637595 };
638596
639 try writeVecSectionHeader(
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 );
597 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
646598 code_section_index = section_index;
647599 section_index += 1;
648600 }
......@@ -682,13 +634,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
682634 }
683635 assert(group_index == f.data_segment_groups.items.len);
684636
685 try writeVecSectionHeader(
686 binary_bytes.items,
687 header_offset,
688 .data,
689 @intCast(binary_bytes.items.len - header_offset - header_size),
690 group_index,
691 );
637 replaceVecSectionHeader(binary_bytes, header_offset, .data, group_index);
692638 data_section_index = section_index;
693639 section_index += 1;
694640 }
......@@ -768,7 +714,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena
768714 };
769715
770716 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
773719 var segments: std.MultiArrayList(NamedIndex) = .empty;
774720 try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());
......@@ -844,10 +790,8 @@ fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
844790}
845791
846792fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
847 // unlike regular section, we don't emit the count
848 const header_size = 1 + 5;
849 try bytes.appendNTimes(gpa, 0, header_size);
850 return @intCast(bytes.items.len - header_size);
793 try bytes.appendNTimes(gpa, 0, section_header_size);
794 return @intCast(bytes.items.len - section_header_size);
851795}
852796
853797fn emitNameSubsection(
......@@ -1106,38 +1050,57 @@ fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index:
11061050 return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);
11071051}
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
11091057fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
1110 // section id + fixed leb contents size + fixed leb vector length
1111 const header_size = 1 + 5 + 5;
1112 try bytes.appendNTimes(gpa, 0, header_size);
1113 return @intCast(bytes.items.len - header_size);
1058 try bytes.appendNTimes(gpa, 0, section_header_reserve_size);
1059 return @intCast(bytes.items.len - section_header_reserve_size);
11141060}
11151061
1116fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
1117 var buf: [1 + 5 + 5]u8 = undefined;
1118 buf[0] = @intFromEnum(section);
1119 leb.writeUnsignedFixed(5, buf[1..6], size);
1120 leb.writeUnsignedFixed(5, buf[6..], items);
1121 buffer[offset..][0..buf.len].* = buf;
1062fn replaceVecSectionHeader(
1063 bytes: *std.ArrayListUnmanaged(u8),
1064 offset: u32,
1065 section: std.wasm.Section,
1066 n_items: u32,
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());
11221076}
11231077
1124fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
1125 try writer.writeByte(limits.flags);
1126 try leb.writeUleb128(writer, limits.min);
1127 if (limits.flags.has_max) try leb.writeUleb128(writer, limits.max);
1078fn emitLimits(
1079 gpa: Allocator,
1080 binary_bytes: *std.ArrayListUnmanaged(u8),
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);
11281086}
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;
11311094 const module_name = memory_import.module_name.slice(wasm);
1132 try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));
1133 try writer.writeAll(module_name);
1095 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len)));
1096 try binary_bytes.appendSlice(gpa, module_name);
11341097
11351098 const name = memory_import.name.slice(wasm);
1136 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
1137 try writer.writeAll(name);
1099 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1100 try binary_bytes.appendSlice(gpa, name);
11381101
1139 try writer.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));
1140 try emitLimits(writer, memory_import.limits());
1102 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
1103 try emitLimits(gpa, binary_bytes, memory_import.limits());
11411104}
11421105
11431106pub 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 {
11661129 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));
11671130}
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
11691138//fn emitLinkSection(
11701139// wasm: *Wasm,
11711140// 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
10401040 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
10411041}
10421042
1043fn skipInit(bytes: []const u8, pos: usize) !usize {
1043pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {
10441044 const opcode = bytes[pos];
1045 const end_pos = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
1045 return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
10461046 .i32_const => readLeb(i32, bytes, pos + 1)[1],
10471047 .i64_const => readLeb(i64, bytes, pos + 1)[1],
10481048 .f32_const => pos + 5,
......@@ -1050,6 +1050,10 @@ fn skipInit(bytes: []const u8, pos: usize) !usize {
10501050 .global_get => readLeb(u32, bytes, pos + 1)[1],
10511051 else => return error.InvalidInitOpcode,
10521052 };
1053}
1054
1055fn skipInit(bytes: []const u8, pos: usize) !usize {
1056 const end_pos = try exprEndPos(bytes, pos);
10531057 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
10541058 if (op != .end) return error.InitExprMissingEnd;
10551059 return final_pos;