authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-31 13:44:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-31 13:44:06+02:00
log4ba0ad295f2b3ff9cc28fd45939f849c3ace5dda
treebfd4f18623b20b582fd97dd5885d7e57e397cfba
parent9f9e51eb110289850ee30c0b4ca895a8861f4c40
parent8627858bbc2fee848e2f3e3ca64dc944f39591e5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12687 from Luukdegram/wasm-extern-globals

wasm-linker: implement non-function extern variables

8 files changed, 73 insertions(+), 22 deletions(-)

src/arch/wasm/CodeGen.zig-2
...@@ -2394,9 +2394,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -2394,9 +2394,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
2394 const decl_index = decl_ref_mut.data.decl_index;2394 const decl_index = decl_ref_mut.data.decl_index;
2395 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);2395 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
2396 }2396 }
2397
2398 const target = self.target;2397 const target = self.target;
2399
2400 switch (ty.zigTypeTag()) {2398 switch (ty.zigTypeTag()) {
2401 .Void => return WValue{ .none = {} },2399 .Void => return WValue{ .none = {} },
2402 .Int => {2400 .Int => {
src/link/Wasm.zig+34-11
...@@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void {...@@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void {
607 }607 }
608}608}
609609
610fn checkUndefinedSymbols(self: *const Wasm) !void {
611 var found_undefined_symbols = false;
612 for (self.undefs.values()) |undef| {
613 const symbol = undef.getSymbol(self);
614 if (symbol.tag == .data) {
615 found_undefined_symbols = true;
616 const file_name = if (undef.file) |file_index| name: {
617 break :name self.objects.items[file_index].name;
618 } else self.name;
619 log.err("could not resolve undefined symbol '{s}'", .{undef.getName(self)});
620 log.err(" defined in '{s}'", .{file_name});
621 }
622 }
623 if (found_undefined_symbols) {
624 return error.UndefinedSymbol;
625 }
626}
627
610pub fn deinit(self: *Wasm) void {628pub fn deinit(self: *Wasm) void {
611 const gpa = self.base.allocator;629 const gpa = self.base.allocator;
612 if (build_options.have_llvm) {630 if (build_options.have_llvm) {
...@@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
783801
784 decl.link.wasm.clear();802 decl.link.wasm.clear();
785803
786 if (decl.isExtern()) {
787 return;
788 }
789
790 if (decl.val.castTag(.function)) |_| {804 if (decl.val.castTag(.function)) |_| {
791 return;805 return;
792 } else if (decl.val.castTag(.extern_fn)) |_| {806 } else if (decl.val.castTag(.extern_fn)) |_| {
793 return;807 return;
794 }808 }
809
810 if (decl.isExtern()) {
811 const variable = decl.getVariable().?;
812 const name = mem.sliceTo(decl.name, 0);
813 return self.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null);
814 }
795 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;815 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
796816
797 var code_writer = std.ArrayList(u8).init(self.base.allocator);817 var code_writer = std.ArrayList(u8).init(self.base.allocator);
...@@ -834,19 +854,18 @@ pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl)...@@ -834,19 +854,18 @@ pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl)
834}854}
835855
836fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {856fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
837 if (code.len == 0) return;
838 const mod = self.base.options.module.?;857 const mod = self.base.options.module.?;
839 const atom: *Atom = &decl.link.wasm;858 const atom: *Atom = &decl.link.wasm;
840 atom.size = @intCast(u32, code.len);
841 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
842 const symbol = &self.symbols.items[atom.sym_index];859 const symbol = &self.symbols.items[atom.sym_index];
843
844 const full_name = try decl.getFullyQualifiedName(mod);860 const full_name = try decl.getFullyQualifiedName(mod);
845 defer self.base.allocator.free(full_name);861 defer self.base.allocator.free(full_name);
846 symbol.name = try self.string_table.put(self.base.allocator, full_name);862 symbol.name = try self.string_table.put(self.base.allocator, full_name);
847 try atom.code.appendSlice(self.base.allocator, code);863 try atom.code.appendSlice(self.base.allocator, code);
848
849 try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {});864 try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {});
865
866 if (code.len == 0) return;
867 atom.size = @intCast(u32, code.len);
868 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
850}869}
851870
852/// From a given symbol location, returns its `wasm.GlobalType`.871/// From a given symbol location, returns its `wasm.GlobalType`.
...@@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport(...@@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport(
1235 .kind = .{ .function = ty_index },1254 .kind = .{ .function = ty_index },
1236 };1255 };
1237 }1256 }
1238 } else @panic("TODO: Implement undefined symbols for non-function declarations");1257 } else {
1258 symbol.tag = .data;
1259 return; // non-functions will not be imported from the runtime, but only resolved during link-time
1260 }
1239}1261}
12401262
1241/// Kind represents the type of an Atom, which is only1263/// Kind represents the type of an Atom, which is only
...@@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void {...@@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void {
1438 if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) {1460 if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) {
1439 continue;1461 continue;
1440 }1462 }
1441 if (symbol.tag == .data or !symbol.requiresImport()) {1463 if (!symbol.requiresImport()) {
1442 continue;1464 continue;
1443 }1465 }
14441466
...@@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2007 }2029 }
20082030
2009 try self.resolveSymbolsInArchives();2031 try self.resolveSymbolsInArchives();
2032 try self.checkUndefinedSymbols();
20102033
2011 // When we finish/error we reset the state of the linker2034 // When we finish/error we reset the state of the linker
2012 // So we can rebuild the binary file on each incremental update2035 // So we can rebuild the binary file on each incremental update
src/link/Wasm/Atom.zig+6-8
...@@ -172,18 +172,16 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -172,18 +172,16 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
172 .R_WASM_MEMORY_ADDR_SLEB,172 .R_WASM_MEMORY_ADDR_SLEB,
173 .R_WASM_MEMORY_ADDR_SLEB64,173 .R_WASM_MEMORY_ADDR_SLEB64,
174 => {174 => {
175 if (symbol.isUndefined() and symbol.isWeak()) {175 std.debug.assert(symbol.tag == .data and !symbol.isUndefined());
176 return 0;
177 }
178 std.debug.assert(symbol.tag == .data);
179 const merge_segment = wasm_bin.base.options.output_mode != .Obj;176 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
180 const segment_info = if (self.file) |object_index| blk: {177 const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc;
178 const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?;
179 const segment_info = if (target_atom.file) |object_index| blk: {
181 break :blk wasm_bin.objects.items[object_index].segment_info;180 break :blk wasm_bin.objects.items[object_index].segment_info;
182 } else wasm_bin.segment_info.items;181 } else wasm_bin.segment_info.items;
183 const segment_name = segment_info[symbol.index].outputName(merge_segment);182 const segment_name = segment_info[symbol.index].outputName(merge_segment);
184 const atom_index = wasm_bin.data_segments.get(segment_name).?;183 const segment_index = wasm_bin.data_segments.get(segment_name).?;
185 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;184 const segment = wasm_bin.segments.items[segment_index];
186 const segment = wasm_bin.segments.items[atom_index];
187 return target_atom.offset + segment.offset + (relocation.addend orelse 0);185 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
188 },186 },
189 .R_WASM_EVENT_INDEX_LEB => return symbol.index,187 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
src/link/Wasm/Symbol.zig+1-1
...@@ -79,9 +79,9 @@ pub const Flag = enum(u32) {...@@ -79,9 +79,9 @@ pub const Flag = enum(u32) {
79/// Verifies if the given symbol should be imported from the79/// Verifies if the given symbol should be imported from the
80/// host environment or not80/// host environment or not
81pub fn requiresImport(self: Symbol) bool {81pub fn requiresImport(self: Symbol) bool {
82 if (self.tag == .data) return false;
82 if (!self.isUndefined()) return false;83 if (!self.isUndefined()) return false;
83 if (self.isWeak()) return false;84 if (self.isWeak()) return false;
84 if (self.tag == .data) return false;
85 // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib85 // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib
8686
87 return true;87 return true;
test/link.zig+6
...@@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {...@@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {
52 .build_modes = true,52 .build_modes = true,
53 .requires_stage2 = true,53 .requires_stage2 = true,
54 });54 });
55
56 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
57 .build_modes = true,
58 .requires_stage2 = true,
59 .use_emulation = true,
60 });
55}61}
5662
57fn addMachOCases(cases: *tests.StandaloneContext) void {63fn addMachOCases(cases: *tests.StandaloneContext) void {
test/link/wasm/extern/build.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("std");
2
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5 const exe = b.addExecutable("extern", "main.zig");
6 exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi });
7 exe.setBuildMode(mode);
8 exe.addCSourceFile("foo.c", &.{});
9 exe.use_llvm = false;
10 exe.use_lld = false;
11
12 const run = exe.runEmulatable();
13 run.expectStdOutEqual("Result: 30");
14
15 const test_step = b.step("test", "Run linker test");
16 test_step.dependOn(&run.step);
17}
test/link/wasm/extern/foo.c created+1
...@@ -0,0 +1 @@
1int foo = 30;
test/link/wasm/extern/main.zig created+8
...@@ -0,0 +1,8 @@
1const std = @import("std");
2
3extern const foo: u32;
4
5pub fn main() void {
6 const std_out = std.io.getStdOut();
7 std_out.writer().print("Result: {d}", .{foo}) catch {};
8}