Skip to content

Commit 08459ff

Browse files
authored
Merge pull request #11933 from Luukdegram/wasm-link-bss
stage2: wasm-linker - Place decls in the correct segment and order segments
2 parents 960c142 + 140bac6 commit 08459ff

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

src/link/Wasm.zig

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,9 +1143,29 @@ pub fn addOrUpdateImport(
11431143
} else @panic("TODO: Implement undefined symbols for non-function declarations");
11441144
}
11451145

1146+
/// Kind represents the type of an Atom, which is only
1147+
/// used to parse a decl into an Atom to define in which section
1148+
/// or segment it should be placed.
11461149
const Kind = union(enum) {
1147-
data: void,
1150+
/// Represents the segment the data symbol should
1151+
/// be inserted into.
1152+
/// TODO: Add TLS segments
1153+
data: enum {
1154+
read_only,
1155+
uninitialized,
1156+
initialized,
1157+
},
11481158
function: FnData,
1159+
1160+
/// Returns the segment name the data kind represents.
1161+
/// Asserts `kind` has its active tag set to `data`.
1162+
fn segmentName(kind: Kind) []const u8 {
1163+
switch (kind.data) {
1164+
.read_only => return ".rodata.",
1165+
.uninitialized => return ".bss.",
1166+
.initialized => return ".data.",
1167+
}
1168+
}
11491169
};
11501170

11511171
/// Parses an Atom and inserts its metadata into the corresponding sections.
@@ -1174,9 +1194,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
11741194
break :result self.code_section_index.?;
11751195
},
11761196
.data => result: {
1177-
// TODO: Add mutables global decls to .bss section instead
11781197
const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
1179-
".rodata.",
1198+
kind.segmentName(),
11801199
self.string_table.get(symbol.name),
11811200
});
11821201
errdefer self.base.allocator.free(segment_name);
@@ -1236,6 +1255,9 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
12361255
}
12371256

12381257
fn allocateAtoms(self: *Wasm) !void {
1258+
// first sort the data segments
1259+
try sortDataSegments(self);
1260+
12391261
var it = self.atoms.iterator();
12401262
while (it.next()) |entry| {
12411263
const segment = &self.segments.items[entry.key_ptr.*];
@@ -1259,6 +1281,36 @@ fn allocateAtoms(self: *Wasm) !void {
12591281
}
12601282
}
12611283

1284+
fn sortDataSegments(self: *Wasm) !void {
1285+
var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
1286+
try new_mapping.ensureUnusedCapacity(self.base.allocator, self.data_segments.count());
1287+
errdefer new_mapping.deinit(self.base.allocator);
1288+
1289+
const keys = try self.base.allocator.dupe([]const u8, self.data_segments.keys());
1290+
defer self.base.allocator.free(keys);
1291+
1292+
const SortContext = struct {
1293+
fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
1294+
return order(lhs) <= order(rhs);
1295+
}
1296+
1297+
fn order(name: []const u8) u8 {
1298+
if (mem.startsWith(u8, name, ".rodata")) return 0;
1299+
if (mem.startsWith(u8, name, ".data")) return 1;
1300+
if (mem.startsWith(u8, name, ".text")) return 2;
1301+
return 3;
1302+
}
1303+
};
1304+
1305+
std.sort.sort([]const u8, keys, {}, SortContext.sort);
1306+
for (keys) |key| {
1307+
const segment_index = self.data_segments.get(key).?;
1308+
new_mapping.putAssumeCapacity(key, segment_index);
1309+
}
1310+
self.data_segments.deinit(self.base.allocator);
1311+
self.data_segments = new_mapping;
1312+
}
1313+
12621314
fn setupImports(self: *Wasm) !void {
12631315
log.debug("Merging imports", .{});
12641316
var discarded_it = self.discarded.keyIterator();
@@ -1722,8 +1774,8 @@ fn populateErrorNameTable(self: *Wasm) !void {
17221774

17231775
// link the atoms with the rest of the binary so they can be allocated
17241776
// and relocations will be performed.
1725-
try self.parseAtom(atom, .data);
1726-
try self.parseAtom(names_atom, .data);
1777+
try self.parseAtom(atom, .{ .data = .read_only });
1778+
try self.parseAtom(names_atom, .{ .data = .read_only });
17271779
}
17281780

17291781
pub fn getDebugInfoIndex(self: *Wasm) !u32 {
@@ -1866,13 +1918,21 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
18661918
const atom = &decl.*.link.wasm;
18671919
if (decl.ty.zigTypeTag() == .Fn) {
18681920
try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
1921+
} else if (decl.getVariable()) |variable| {
1922+
if (!variable.is_mutable) {
1923+
try self.parseAtom(atom, .{ .data = .read_only });
1924+
} else if (variable.init.isUndefDeep()) {
1925+
try self.parseAtom(atom, .{ .data = .uninitialized });
1926+
} else {
1927+
try self.parseAtom(atom, .{ .data = .initialized });
1928+
}
18691929
} else {
1870-
try self.parseAtom(atom, .data);
1930+
try self.parseAtom(atom, .{ .data = .read_only });
18711931
}
18721932

18731933
// also parse atoms for a decl's locals
18741934
for (atom.locals.items) |*local_atom| {
1875-
try self.parseAtom(local_atom, .data);
1935+
try self.parseAtom(local_atom, .{ .data = .read_only });
18761936
}
18771937
}
18781938

@@ -2167,7 +2227,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
21672227
while (it.next()) |entry| {
21682228
// do not output 'bss' section unless we import memory and therefore
21692229
// want to guarantee the data is zero initialized
2170-
if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue;
2230+
if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
21712231
segment_count += 1;
21722232
const atom_index = entry.value_ptr.*;
21732233
var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst();
@@ -2310,8 +2370,13 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
23102370
}
23112371
}
23122372
// data segments are already 'ordered'
2313-
for (self.data_segments.keys()) |key, index| {
2314-
segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });
2373+
var data_segment_index: u32 = 0;
2374+
for (self.data_segments.keys()) |key| {
2375+
// bss section is not emitted when this condition holds true, so we also
2376+
// do not output a name for it.
2377+
if (!self.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;
2378+
segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
2379+
data_segment_index += 1;
23152380
}
23162381

23172382
std.sort.sort(Name, funcs.values(), {}, Name.lessThan);

src/link/Wasm/types.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,15 @@ pub const Segment = struct {
125125
/// Bitfield containing flags for a segment
126126
flags: u32,
127127

128+
/// Returns the name as how it will be output into the final object
129+
/// file or binary. When `merge_segments` is true, this will return the
130+
/// short name. i.e. ".rodata". When false, it returns the entire name instead.
128131
pub fn outputName(self: Segment, merge_segments: bool) []const u8 {
129132
if (!merge_segments) return self.name;
130133
if (std.mem.startsWith(u8, self.name, ".rodata.")) {
131134
return ".rodata";
132135
} else if (std.mem.startsWith(u8, self.name, ".text.")) {
133136
return ".text";
134-
} else if (std.mem.startsWith(u8, self.name, ".rodata.")) {
135-
return ".rodata";
136137
} else if (std.mem.startsWith(u8, self.name, ".data.")) {
137138
return ".data";
138139
} else if (std.mem.startsWith(u8, self.name, ".bss.")) {

0 commit comments

Comments
 (0)