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
signaturelock-open 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 {
651651 }
652652}
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 {
655660 const cpu_features = wasm.base.options.target.cpu.features;
656661 const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects.
657662 var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);
......@@ -755,6 +760,13 @@ fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void {
755760 if (!valid_feature_set) {
756761 return error.InvalidFeatureSet;
757762 }
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 }
758770}
759771
760772fn checkUndefinedSymbols(wasm: *const Wasm) !void {
......@@ -2264,7 +2276,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
22642276 try wasm.resolveSymbolsInObject(@intCast(u16, object_index));
22652277 }
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);
22682282 try wasm.resolveSymbolsInArchives();
22692283 try wasm.checkUndefinedSymbols();
22702284
......@@ -2710,6 +2724,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
27102724 }
27112725
27122726 try emitProducerSection(&binary_bytes);
2727 if (emit_features_count > 0) {
2728 try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count);
2729 }
27132730 }
27142731
27152732 // Only when writing all sections executed properly we write the magic
......@@ -2802,6 +2819,32 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void {
28022819 );
28032820}
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
28052848fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void {
28062849 const Name = struct {
28072850 index: u32,