authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-12 14:36:33+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-12 14:36:33+02:00
log8033767082f2178416fba8cb4a4b03fef961d318
tree820252e8957cce74e365496d73debd6950e29f9c
parent7d2e14267985df0226a5deee96d0c17c94bf6eb2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

wasm-linker: Implement linker tests (#12006)

* test/link: initial wasm support This adds basic parsing and dumping of wasm section so they can be tested using the new linker-test infrastructure. * test/link: all wasm sections parsing and dumping We now parse and dump all sections for the wasm binary format. Currently, this only dumps the name of a custom section. Later this should also dump symbol table, name, linking metadata and relocations. All of those live within the custom sections. * Add wasm linker test This also fixes a parser mistake in reading the flags. * test/link: implement linker tests wasm & fixes Adds several test cases to test the wasm self-hosted linker. This also introduces fixes that were caught during the implementation of those tests. * test-runner: obey omit_stage2 for standalone When a standalone test requires stage2, but stage2 is omit from the compiler, such test case will not be included as part of the test suite that is being ran. This is to support CI's where we omit stage2 to lower the memory usage.

16 files changed, 519 insertions(+), 9 deletions(-)

build.zig+2-2
......@@ -481,8 +481,8 @@ pub fn build(b: *Builder) !void {
481481 ));
482482
483483 toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
484 toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target));
485 toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk));
484 toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target, omit_stage2));
485 toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, omit_stage2));
486486 toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
487487 toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes));
488488 toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
ci/azure/macos_script+1-1
......@@ -76,7 +76,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk
7676release/bin/zig build docs -Denable-macos-sdk
7777release/bin/zig build test-fmt -Denable-macos-sdk
7878release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded
79release/bin/zig build test-link -Denable-macos-sdk
79release/bin/zig build test-link -Denable-macos-sdk -Domit-stage2
8080
8181if [ "${BUILD_REASON}" != "PullRequest" ]; then
8282 mv ../LICENSE release/
lib/std/build.zig+9
......@@ -1618,6 +1618,7 @@ pub const LibExeObjStep = struct {
16181618 want_lto: ?bool = null,
16191619 use_stage1: ?bool = null,
16201620 use_llvm: ?bool = null,
1621 use_lld: ?bool = null,
16211622 ofmt: ?std.Target.ObjectFormat = null,
16221623
16231624 output_path_source: GeneratedFile,
......@@ -2474,6 +2475,14 @@ pub const LibExeObjStep = struct {
24742475 }
24752476 }
24762477
2478 if (self.use_lld) |use_lld| {
2479 if (use_lld) {
2480 try zig_args.append("-fLLD");
2481 } else {
2482 try zig_args.append("-fno-LLD");
2483 }
2484 }
2485
24772486 if (self.ofmt) |ofmt| {
24782487 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
24792488 }
lib/std/build/CheckObjectStep.zig+296-1
......@@ -265,7 +265,10 @@ fn make(step: *Step) !void {
265265 }),
266266 .elf => @panic("TODO elf parser"),
267267 .coff => @panic("TODO coff parser"),
268 .wasm => @panic("TODO wasm parser"),
268 .wasm => try WasmDumper.parseAndDump(contents, .{
269 .gpa = gpa,
270 .dump_symtab = self.dump_symtab,
271 }),
269272 else => unreachable,
270273 };
271274
......@@ -522,3 +525,295 @@ const MachODumper = struct {
522525 }
523526 }
524527};
528
529const WasmDumper = struct {
530 const symtab_label = "symbols";
531
532 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
533 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
534 if (opts.dump_symtab) {
535 @panic("TODO: Implement symbol table parsing and dumping");
536 }
537
538 var fbs = std.io.fixedBufferStream(bytes);
539 const reader = fbs.reader();
540
541 const buf = try reader.readBytesNoEof(8);
542 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
543 return error.InvalidMagicByte;
544 }
545 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
546 return error.UnsupportedWasmVersion;
547 }
548
549 var output = std.ArrayList(u8).init(gpa);
550 errdefer output.deinit();
551 const writer = output.writer();
552
553 while (reader.readByte()) |current_byte| {
554 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
555 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
556 return err;
557 };
558
559 const section_length = try std.leb.readULEB128(u32, reader);
560 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
561 fbs.pos += section_length;
562 } else |_| {} // reached end of stream
563
564 return output.toOwnedSlice();
565 }
566
567 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
568 var fbs = std.io.fixedBufferStream(data);
569 const reader = fbs.reader();
570
571 try writer.print(
572 \\Section {s}
573 \\size {d}
574 , .{ @tagName(section), data.len });
575
576 switch (section) {
577 .type,
578 .import,
579 .function,
580 .table,
581 .memory,
582 .global,
583 .@"export",
584 .element,
585 .code,
586 .data,
587 => {
588 const entries = try std.leb.readULEB128(u32, reader);
589 try writer.print("\nentries {d}\n", .{entries});
590 try dumpSection(section, data[fbs.pos..], entries, writer);
591 },
592 .custom => {
593 const name_length = try std.leb.readULEB128(u32, reader);
594 const name = data[fbs.pos..][0..name_length];
595 fbs.pos += name_length;
596 try writer.print("\nname {s}\n", .{name});
597
598 if (mem.eql(u8, name, "name")) {
599 try parseDumpNames(reader, writer, data);
600 }
601 // TODO: Implement parsing and dumping other custom sections (such as relocations)
602 },
603 .start => {
604 const start = try std.leb.readULEB128(u32, reader);
605 try writer.print("\nstart {d}\n", .{start});
606 },
607 else => {}, // skip unknown sections
608 }
609 }
610
611 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
612 var fbs = std.io.fixedBufferStream(data);
613 const reader = fbs.reader();
614
615 switch (section) {
616 .type => {
617 var i: u32 = 0;
618 while (i < entries) : (i += 1) {
619 const func_type = try reader.readByte();
620 if (func_type != std.wasm.function_type) {
621 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
622 return error.UnexpectedByte;
623 }
624 const params = try std.leb.readULEB128(u32, reader);
625 try writer.print("params {d}\n", .{params});
626 var index: u32 = 0;
627 while (index < params) : (index += 1) {
628 try parseDumpType(std.wasm.Valtype, reader, writer);
629 } else index = 0;
630 const returns = try std.leb.readULEB128(u32, reader);
631 try writer.print("returns {d}\n", .{returns});
632 while (index < returns) : (index += 1) {
633 try parseDumpType(std.wasm.Valtype, reader, writer);
634 }
635 }
636 },
637 .import => {
638 var i: u32 = 0;
639 while (i < entries) : (i += 1) {
640 const module_name_len = try std.leb.readULEB128(u32, reader);
641 const module_name = data[fbs.pos..][0..module_name_len];
642 fbs.pos += module_name_len;
643 const name_len = try std.leb.readULEB128(u32, reader);
644 const name = data[fbs.pos..][0..name_len];
645 fbs.pos += name_len;
646
647 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
648 std.debug.print("Invalid import kind\n", .{});
649 return err;
650 };
651
652 try writer.print(
653 \\module {s}
654 \\name {s}
655 \\kind {s}
656 , .{ module_name, name, @tagName(kind) });
657 try writer.writeByte('\n');
658 switch (kind) {
659 .function => {
660 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
661 },
662 .memory => {
663 try parseDumpLimits(reader, writer);
664 },
665 .global => {
666 try parseDumpType(std.wasm.Valtype, reader, writer);
667 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
668 },
669 .table => {
670 try parseDumpType(std.wasm.RefType, reader, writer);
671 try parseDumpLimits(reader, writer);
672 },
673 }
674 }
675 },
676 .function => {
677 var i: u32 = 0;
678 while (i < entries) : (i += 1) {
679 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
680 }
681 },
682 .table => {
683 var i: u32 = 0;
684 while (i < entries) : (i += 1) {
685 try parseDumpType(std.wasm.RefType, reader, writer);
686 try parseDumpLimits(reader, writer);
687 }
688 },
689 .memory => {
690 var i: u32 = 0;
691 while (i < entries) : (i += 1) {
692 try parseDumpLimits(reader, writer);
693 }
694 },
695 .global => {
696 var i: u32 = 0;
697 while (i < entries) : (i += 1) {
698 try parseDumpType(std.wasm.Valtype, reader, writer);
699 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
700 try parseDumpInit(reader, writer);
701 }
702 },
703 .@"export" => {
704 var i: u32 = 0;
705 while (i < entries) : (i += 1) {
706 const name_len = try std.leb.readULEB128(u32, reader);
707 const name = data[fbs.pos..][0..name_len];
708 fbs.pos += name_len;
709 const kind_byte = try std.leb.readULEB128(u8, reader);
710 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
711 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
712 return err;
713 };
714 const index = try std.leb.readULEB128(u32, reader);
715 try writer.print(
716 \\name {s}
717 \\kind {s}
718 \\index {d}
719 , .{ name, @tagName(kind), index });
720 try writer.writeByte('\n');
721 }
722 },
723 .element => {
724 var i: u32 = 0;
725 while (i < entries) : (i += 1) {
726 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
727 try parseDumpInit(reader, writer);
728
729 const function_indexes = try std.leb.readULEB128(u32, reader);
730 var function_index: u32 = 0;
731 try writer.print("indexes {d}\n", .{function_indexes});
732 while (function_index < function_indexes) : (function_index += 1) {
733 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
734 }
735 }
736 },
737 .code => {}, // code section is considered opaque to linker
738 .data => {
739 var i: u32 = 0;
740 while (i < entries) : (i += 1) {
741 const index = try std.leb.readULEB128(u32, reader);
742 try writer.print("memory index 0x{x}\n", .{index});
743 try parseDumpInit(reader, writer);
744 const size = try std.leb.readULEB128(u32, reader);
745 try writer.print("size {d}\n", .{size});
746 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
747 }
748 },
749 else => unreachable,
750 }
751 }
752
753 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
754 const type_byte = try reader.readByte();
755 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
756 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
757 return err;
758 };
759 try writer.print("type {s}\n", .{@tagName(valtype)});
760 }
761
762 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
763 const flags = try std.leb.readULEB128(u8, reader);
764 const min = try std.leb.readULEB128(u32, reader);
765
766 try writer.print("min {x}\n", .{min});
767 if (flags != 0) {
768 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
769 }
770 }
771
772 fn parseDumpInit(reader: anytype, writer: anytype) !void {
773 const byte = try std.leb.readULEB128(u8, reader);
774 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
775 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
776 return err;
777 };
778 switch (opcode) {
779 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
780 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
781 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
782 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
783 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
784 else => unreachable,
785 }
786 const end_opcode = try std.leb.readULEB128(u8, reader);
787 if (end_opcode != std.wasm.opcode(.end)) {
788 std.debug.print("expected 'end' opcode in init expression\n", .{});
789 return error.MissingEndOpcode;
790 }
791 }
792
793 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
794 while (reader.context.pos < data.len) {
795 try parseDumpType(std.wasm.NameSubsection, reader, writer);
796 const size = try std.leb.readULEB128(u32, reader);
797 const entries = try std.leb.readULEB128(u32, reader);
798 try writer.print(
799 \\size {d}
800 \\names {d}
801 , .{ size, entries });
802 try writer.writeByte('\n');
803 var i: u32 = 0;
804 while (i < entries) : (i += 1) {
805 const index = try std.leb.readULEB128(u32, reader);
806 const name_len = try std.leb.readULEB128(u32, reader);
807 const pos = reader.context.pos;
808 const name = data[pos..][0..name_len];
809 reader.context.pos += name_len;
810
811 try writer.print(
812 \\index {d}
813 \\name {s}
814 , .{ index, name });
815 try writer.writeByte('\n');
816 }
817 }
818 }
819};
src/link.zig+4-4
......@@ -351,7 +351,7 @@ pub const File = struct {
351351
352352 pub fn makeWritable(base: *File) !void {
353353 switch (base.tag) {
354 .coff, .elf, .macho, .plan9 => {
354 .coff, .elf, .macho, .plan9, .wasm => {
355355 if (base.file != null) return;
356356 const emit = base.options.emit orelse return;
357357 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
......@@ -360,7 +360,7 @@ pub const File = struct {
360360 .mode = determineMode(base.options),
361361 });
362362 },
363 .c, .wasm, .spirv, .nvptx => {},
363 .c, .spirv, .nvptx => {},
364364 }
365365 }
366366
......@@ -394,7 +394,7 @@ pub const File = struct {
394394 base.file = null;
395395 }
396396 },
397 .coff, .elf, .plan9 => if (base.file) |f| {
397 .coff, .elf, .plan9, .wasm => if (base.file) |f| {
398398 if (base.intermediary_basename != null) {
399399 // The file we have open is not the final file that we want to
400400 // make executable, so we don't have to close it.
......@@ -403,7 +403,7 @@ pub const File = struct {
403403 f.close();
404404 base.file = null;
405405 },
406 .c, .wasm, .spirv, .nvptx => {},
406 .c, .spirv, .nvptx => {},
407407 }
408408 }
409409
src/link/Wasm.zig+10-1
......@@ -1206,6 +1206,14 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
12061206 };
12071207 symbol.tag = .data;
12081208
1209 // when creating an object file, or importing memory and the data belongs in the .bss segment
1210 // we set the entire region of it to zeroes.
1211 // We do not have to do this when exporting the memory (the default) because the runtime
1212 // will do it for us, and we do not emit the bss segment at all.
1213 if ((self.base.options.output_mode == .Obj or self.base.options.import_memory) and kind.data == .uninitialized) {
1214 std.mem.set(u8, atom.code.items, 0);
1215 }
1216
12091217 const should_merge = self.base.options.output_mode != .Obj;
12101218 const gop = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(should_merge));
12111219 if (gop.found_existing) {
......@@ -2014,9 +2022,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
20142022 }
20152023
20162024 if (import_memory) {
2025 const mem_name = if (is_obj) "__linear_memory" else "memory";
20172026 const mem_imp: types.Import = .{
20182027 .module_name = try self.string_table.put(self.base.allocator, self.host_name),
2019 .name = try self.string_table.put(self.base.allocator, "__linear_memory"),
2028 .name = try self.string_table.put(self.base.allocator, mem_name),
20202029 .kind = .{ .memory = self.memories.limits },
20212030 };
20222031 try self.emitImport(writer, mem_imp);
test/link.zig+20
......@@ -27,6 +27,26 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
2727 .build_modes = true,
2828 });
2929
30 cases.addBuildFile("test/link/wasm/type/build.zig", .{
31 .build_modes = true,
32 .requires_stage2 = true,
33 });
34
35 cases.addBuildFile("test/link/wasm/segments/build.zig", .{
36 .build_modes = true,
37 .requires_stage2 = true,
38 });
39
40 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{
41 .build_modes = true,
42 .requires_stage2 = true,
43 });
44
45 cases.addBuildFile("test/link/wasm/bss/build.zig", .{
46 .build_modes = true,
47 .requires_stage2 = true,
48 });
49
3050 if (builtin.os.tag == .macos) {
3151 cases.addBuildFile("test/link/macho/entry/build.zig", .{
3252 .build_modes = true,
test/link/wasm/bss/build.zig created+41
......@@ -0,0 +1,41 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
13 lib.use_llvm = false;
14 lib.use_stage1 = false;
15 lib.use_lld = false;
16 // to make sure the bss segment is emitted, we must import memory
17 lib.import_memory = true;
18 lib.install();
19
20 const check_lib = lib.checkObject(.wasm);
21
22 // since we import memory, make sure it exists with the correct naming
23 check_lib.checkStart("Section import");
24 check_lib.checkNext("entries 1");
25 check_lib.checkNext("module env"); // default module name is "env"
26 check_lib.checkNext("name memory"); // as per linker specification
27
28 // since we are importing memory, ensure it's not exported
29 check_lib.checkStart("Section export");
30 check_lib.checkNext("entries 1"); // we're exporting function 'foo' so only 1 entry
31
32 // validate the name of the stack pointer
33 check_lib.checkStart("Section custom");
34 check_lib.checkNext("type data_segment");
35 check_lib.checkNext("names 2");
36 check_lib.checkNext("index 0");
37 check_lib.checkNext("name .rodata");
38 check_lib.checkNext("index 1"); // bss section always last
39 check_lib.checkNext("name .bss");
40 test_step.dependOn(&check_lib.step);
41}
test/link/wasm/bss/lib.zig created+5
......@@ -0,0 +1,5 @@
1pub var bss: u32 = undefined;
2
3export fn foo() void {
4 _ = bss;
5}
test/link/wasm/segments/build.zig created+31
......@@ -0,0 +1,31 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
13 lib.use_llvm = false;
14 lib.use_stage1 = false;
15 lib.use_lld = false;
16 lib.install();
17
18 const check_lib = lib.checkObject(.wasm);
19 check_lib.checkStart("Section data");
20 check_lib.checkNext("entries 2"); // rodata & data, no bss because we're exporting memory
21
22 check_lib.checkStart("Section custom");
23 check_lib.checkStart("name name"); // names custom section
24 check_lib.checkStart("type data_segment");
25 check_lib.checkNext("names 2");
26 check_lib.checkNext("index 0");
27 check_lib.checkNext("name .rodata");
28 check_lib.checkNext("index 1");
29 check_lib.checkNext("name .data");
30 test_step.dependOn(&check_lib.step);
31}
test/link/wasm/segments/lib.zig created+9
......@@ -0,0 +1,9 @@
1pub const rodata: u32 = 5;
2pub var data: u32 = 10;
3pub var bss: u32 = undefined;
4
5export fn foo() void {
6 _ = rodata;
7 _ = data;
8 _ = bss;
9}
test/link/wasm/stack_pointer/build.zig created+41
......@@ -0,0 +1,41 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
13 lib.use_llvm = false;
14 lib.use_stage1 = false;
15 lib.use_lld = false;
16 lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
17 lib.install();
18
19 const check_lib = lib.checkObject(.wasm);
20
21 // ensure global exists and its initial value is equal to explitic stack size
22 check_lib.checkStart("Section global");
23 check_lib.checkNext("entries 1");
24 check_lib.checkNext("type i32"); // on wasm32 the stack pointer must be i32
25 check_lib.checkNext("mutable true"); // must be able to mutate the stack pointer
26 check_lib.checkNext("i32.const {stack_pointer}");
27 check_lib.checkComputeCompare("stack_pointer", .{ .op = .eq, .value = .{ .literal = lib.stack_size.? } });
28
29 // validate memory section starts after virtual stack
30 check_lib.checkNext("Section data");
31 check_lib.checkNext("i32.const {data_start}");
32 check_lib.checkComputeCompare("data_start", .{ .op = .eq, .value = .{ .variable = "stack_pointer" } });
33
34 // validate the name of the stack pointer
35 check_lib.checkStart("Section custom");
36 check_lib.checkNext("type global");
37 check_lib.checkNext("names 1");
38 check_lib.checkNext("index 0");
39 check_lib.checkNext("name __stack_pointer");
40 test_step.dependOn(&check_lib.step);
41}
test/link/wasm/stack_pointer/lib.zig created+1
......@@ -0,0 +1 @@
1export fn foo() void {}
test/link/wasm/type/build.zig created+32
......@@ -0,0 +1,32 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());
9
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
13 lib.use_llvm = false;
14 lib.use_stage1 = false;
15 lib.use_lld = false;
16 lib.install();
17
18 const check_lib = lib.checkObject(.wasm);
19 check_lib.checkStart("Section type");
20 // only 2 entries, although we have 3 functions.
21 // This is to test functions with the same function signature
22 // have their types deduplicated.
23 check_lib.checkNext("entries 2");
24 check_lib.checkNext("params 1");
25 check_lib.checkNext("type i32");
26 check_lib.checkNext("returns 1");
27 check_lib.checkNext("type i64");
28 check_lib.checkNext("params 0");
29 check_lib.checkNext("returns 0");
30
31 test_step.dependOn(&check_lib.step);
32}
test/link/wasm/type/lib.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo(x: u32) u64 {
2 return bar(x);
3}
4
5fn bar(x: u32) u64 {
6 y();
7 return x;
8}
9
10fn y() void {}
test/tests.zig+7
......@@ -462,6 +462,7 @@ pub fn addStandaloneTests(
462462 skip_non_native: bool,
463463 enable_macos_sdk: bool,
464464 target: std.zig.CrossTarget,
465 omit_stage2: bool,
465466) *build.Step {
466467 const cases = b.allocator.create(StandaloneContext) catch unreachable;
467468 cases.* = StandaloneContext{
......@@ -473,6 +474,7 @@ pub fn addStandaloneTests(
473474 .skip_non_native = skip_non_native,
474475 .enable_macos_sdk = enable_macos_sdk,
475476 .target = target,
477 .omit_stage2 = omit_stage2,
476478 };
477479
478480 standalone.addCases(cases);
......@@ -485,6 +487,7 @@ pub fn addLinkTests(
485487 test_filter: ?[]const u8,
486488 modes: []const Mode,
487489 enable_macos_sdk: bool,
490 omit_stage2: bool,
488491) *build.Step {
489492 const cases = b.allocator.create(StandaloneContext) catch unreachable;
490493 cases.* = StandaloneContext{
......@@ -496,6 +499,7 @@ pub fn addLinkTests(
496499 .skip_non_native = true,
497500 .enable_macos_sdk = enable_macos_sdk,
498501 .target = .{},
502 .omit_stage2 = omit_stage2,
499503 };
500504 link.addCases(cases);
501505 return cases.step;
......@@ -957,6 +961,7 @@ pub const StandaloneContext = struct {
957961 skip_non_native: bool,
958962 enable_macos_sdk: bool,
959963 target: std.zig.CrossTarget,
964 omit_stage2: bool,
960965
961966 pub fn addC(self: *StandaloneContext, root_src: []const u8) void {
962967 self.addAllArgs(root_src, true);
......@@ -970,10 +975,12 @@ pub const StandaloneContext = struct {
970975 build_modes: bool = false,
971976 cross_targets: bool = false,
972977 requires_macos_sdk: bool = false,
978 requires_stage2: bool = false,
973979 }) void {
974980 const b = self.b;
975981
976982 if (features.requires_macos_sdk and !self.enable_macos_sdk) return;
983 if (features.requires_stage2 and self.omit_stage2) return;
977984
978985 const annotated_case_name = b.fmt("build {s}", .{build_file});
979986 if (self.test_filter) |filter| {