diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index ed07756345..da9a878720 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -651,7 +651,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { } } -fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void { +fn validateFeatures( + wasm: *const Wasm, + arena: Allocator, + to_emit: *[@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool, + emit_features_count: *u32, +) !void { const cpu_features = wasm.base.options.target.cpu.features; const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects. var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena); @@ -755,6 +760,13 @@ fn validateFeatures(wasm: *const Wasm, arena: Allocator) !void { if (!valid_feature_set) { return error.InvalidFeatureSet; } + + if (allowed.count() > 0) { + emit_features_count.* = allowed.count(); + for (to_emit) |*feature_enabled, feature_index| { + feature_enabled.* = allowed.contains(@intToEnum(std.Target.wasm.Feature, feature_index)); + } + } } fn checkUndefinedSymbols(wasm: *const Wasm) !void { @@ -2264,7 +2276,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.resolveSymbolsInObject(@intCast(u16, object_index)); } - try wasm.validateFeatures(arena); + var emit_features_count: u32 = 0; + var enabled_features: [@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool = undefined; + try wasm.validateFeatures(arena, &enabled_features, &emit_features_count); try wasm.resolveSymbolsInArchives(); try wasm.checkUndefinedSymbols(); @@ -2710,6 +2724,9 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try emitProducerSection(&binary_bytes); + if (emit_features_count > 0) { + try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count); + } } // Only when writing all sections executed properly we write the magic @@ -2802,6 +2819,32 @@ fn emitProducerSection(binary_bytes: *std.ArrayList(u8)) !void { ); } +fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []const bool, features_count: u32) !void { + const header_offset = try reserveCustomSectionHeader(binary_bytes); + + const writer = binary_bytes.writer(); + const target_features = "target_features"; + try leb.writeULEB128(writer, @intCast(u32, target_features.len)); + try writer.writeAll(target_features); + + try leb.writeULEB128(writer, features_count); + for (enabled_features) |enabled, feature_index| { + if (enabled) { + const feature: types.Feature = .{ .prefix = .used, .tag = @intToEnum(types.Feature.Tag, feature_index) }; + try leb.writeULEB128(writer, @enumToInt(feature.prefix)); + const string = feature.toString(); + try leb.writeULEB128(writer, @intCast(u32, string.len)); + try writer.writeAll(string); + } + } + + try writeCustomSectionHeader( + binary_bytes.items, + header_offset, + @intCast(u32, binary_bytes.items.len - header_offset - 6), + ); +} + fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { const Name = struct { index: u32,