authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-20 16:58:00+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-25 20:48:08+02:00
log777bcbf96871a0250664b9cabdea5dbf51e0e64d
treed32afcefc66379cebce92a2414887d6e84db7d25
parent85b669d497641de383070353d50a6e4fd30abd49
signature Commit is signed but in an unrecognized format.

wasm-linker: emit `target_features` section

When the result is not being stripped, we emit the `target_features` section based on all the used features. This includes features inferred from linked object files. Considering we know all possible features upfront, we can use an array and therefore do not have to dynamically allocate memory. Using this trick we can also easily order all features based the same ordering as found in `std.Target.wasm` which is the same ordering used by LLVM and the like.

1 files changed, 45 insertions(+), 2 deletions(-)

src/link/Wasm.zig+45-2
...@@ -651,7 +651,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -651,7 +651,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
651 }651 }
652}652}
653653
654fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void {654fn validateFeatures(
655 wasm: *const Wasm,
656 arena: Allocator,
657 to_emit: *[@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool,
658 emit_features_count: *u32,
659) !void {
655 const cpu_features = wasm.base.options.target.cpu.features;660 const cpu_features = wasm.base.options.target.cpu.features;
656 const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.661 const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
657 var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);662 var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);
...@@ -755,6 +760,13 @@ fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void {...@@ -755,6 +760,13 @@ fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void {
755 if (!valid_feature_set) {760 if (!valid_feature_set) {
756 return error.InvalidFeatureSet;761 return error.InvalidFeatureSet;
757 }762 }
763
764 if (allowed.count() > 0) {
765 emit_features_count.* = allowed.count();
766 for (to_emit) |*feature_enabled, feature_index| {
767 feature_enabled.* = allowed.contains(@intToEnum(std.Target.wasm.Feature, feature_index));
768 }
769 }
758}770}
759771
760fn checkUndefinedSymbols(wasm: *const Wasm) !void {772fn checkUndefinedSymbols(wasm: *const Wasm) !void {
...@@ -2264,7 +2276,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2264,7 +2276,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2264 try wasm.resolveSymbolsInObject(@intCast(u16, object_index));2276 try wasm.resolveSymbolsInObject(@intCast(u16, object_index));
2265 }2277 }
22662278
2267 try wasm.validateFeatures(arena);2279 var emit_features_count: u32 = 0;
2280 var enabled_features: [@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool = undefined;
2281 try wasm.validateFeatures(arena, &enabled_features, &emit_features_count);
2268 try wasm.resolveSymbolsInArchives();2282 try wasm.resolveSymbolsInArchives();
2269 try wasm.checkUndefinedSymbols();2283 try wasm.checkUndefinedSymbols();
22702284
...@@ -2710,6 +2724,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2710,6 +2724,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2710 }2724 }
27112725
2712 try emitProducerSection(&binary_bytes);2726 try emitProducerSection(&binary_bytes);
2727 if (emit_features_count > 0) {
2728 try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count);
2729 }
2713 }2730 }
27142731
2715 // Only when writing all sections executed properly we write the magic2732 // Only when writing all sections executed properly we write the magic
...@@ -2802,6 +2819,32 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {...@@ -2802,6 +2819,32 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
2802 );2819 );
2803}2820}
28042821
2822fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void {
2823 const header_offset = try reserveCustomSectionHeader(binary_bytes);
2824
2825 const writer = binary_bytes.writer();
2826 const target_features = "target_features";
2827 try leb.writeULEB128(writer, @intCast(u32, target_features.len));
2828 try writer.writeAll(target_features);
2829
2830 try leb.writeULEB128(writer, features_count);
2831 for (enabled_features) |enabled, feature_index| {
2832 if (enabled) {
2833 const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) };
2834 try leb.writeULEB128(writer, @enumToInt(feature.prefix));
2835 const string = feature.toString();
2836 try leb.writeULEB128(writer, @intCast(u32, string.len));
2837 try writer.writeAll(string);
2838 }
2839 }
2840
2841 try writeCustomSectionHeader(
2842 binary_bytes.items,
2843 header_offset,
2844 @intCast(u32, binary_bytes.items.len - header_offset - 6),
2845 );
2846}
2847
2805fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {2848fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
2806 const Name = struct {2849 const Name = struct {
2807 index: u32,2850 index: u32,