authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-23 20:02:25+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-25 20:48:09+02:00
log3d1d19f3877190db42544acf9e0ed26784ba82ba
tree71d7f6ac0907474f3bca919863aa41f84afade2e
parent2f41109cc445c450029ea81c0da6873f32fa0981
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: seperate linker -and cpu features

The list of features a Wasm object/binary file can emit can differ from the list of cpu features. The reason for this is because the "target_features" section also contains linker features. An example of this is the "shared-mem" feature, which is a feature for the linker and not that of the cpu target as defined by LLVM.

2 files changed, 32 insertions(+), 10 deletions(-)

src/link/Wasm.zig+9-9
...@@ -654,15 +654,15 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -654,15 +654,15 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
654fn validateFeatures(654fn validateFeatures(
655 wasm: *const Wasm,655 wasm: *const Wasm,
656 arena: Allocator,656 arena: Allocator,
657 to_emit: *[@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool,657 to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
658 emit_features_count: *u32,658 emit_features_count: *u32,
659) !void {659) !void {
660 const cpu_features = wasm.base.options.target.cpu.features;660 const cpu_features = wasm.base.options.target.cpu.features;
661 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.
662 var allowed = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);662 var allowed = std.AutoHashMap(types.Feature.Tag, void).init(arena);
663 var used = std.AutoArrayHashMap(std.Target.wasm.Feature, []const u8).init(arena);663 var used = std.AutoArrayHashMap(types.Feature.Tag, []const u8).init(arena);
664 var disallowed = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena);664 var disallowed = std.AutoHashMap(types.Feature.Tag, []const u8).init(arena);
665 var required = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena);665 var required = std.AutoHashMap(types.Feature.Tag, []const u8).init(arena);
666666
667 // when false, we fail linking. We only verify this after a loop to catch all invalid features.667 // when false, we fail linking. We only verify this after a loop to catch all invalid features.
668 var valid_feature_set = true;668 var valid_feature_set = true;
...@@ -674,7 +674,7 @@ fn validateFeatures(...@@ -674,7 +674,7 @@ fn validateFeatures(
674 // std.builtin.Type.EnumField674 // std.builtin.Type.EnumField
675 inline for (@typeInfo(std.Target.wasm.Feature).Enum.fields) |feature_field| {675 inline for (@typeInfo(std.Target.wasm.Feature).Enum.fields) |feature_field| {
676 if (cpu_features.isEnabled(feature_field.value)) {676 if (cpu_features.isEnabled(feature_field.value)) {
677 allowed.putAssumeCapacityNoClobber(@intToEnum(std.Target.wasm.Feature, feature_field.value), {});677 allowed.putAssumeCapacityNoClobber(@intToEnum(types.Feature.Tag, feature_field.value), {});
678 }678 }
679 }679 }
680 }680 }
...@@ -730,7 +730,7 @@ fn validateFeatures(...@@ -730,7 +730,7 @@ fn validateFeatures(
730730
731 // For each linked object, validate the required and disallowed features731 // For each linked object, validate the required and disallowed features
732 for (wasm.objects.items) |object| {732 for (wasm.objects.items) |object| {
733 var object_used_features = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);733 var object_used_features = std.AutoHashMap(types.Feature.Tag, void).init(arena);
734 try object_used_features.ensureTotalCapacity(@intCast(u32, object.features.len));734 try object_used_features.ensureTotalCapacity(@intCast(u32, object.features.len));
735 for (object.features) |feature| {735 for (object.features) |feature| {
736 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.736 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
...@@ -764,7 +764,7 @@ fn validateFeatures(...@@ -764,7 +764,7 @@ fn validateFeatures(
764 if (allowed.count() > 0) {764 if (allowed.count() > 0) {
765 emit_features_count.* = allowed.count();765 emit_features_count.* = allowed.count();
766 for (to_emit) |*feature_enabled, feature_index| {766 for (to_emit) |*feature_enabled, feature_index| {
767 feature_enabled.* = allowed.contains(@intToEnum(std.Target.wasm.Feature, feature_index));767 feature_enabled.* = allowed.contains(@intToEnum(types.Feature.Tag, feature_index));
768 }768 }
769 }769 }
770}770}
...@@ -2277,7 +2277,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2277,7 +2277,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2277 }2277 }
22782278
2279 var emit_features_count: u32 = 0;2279 var emit_features_count: u32 = 0;
2280 var enabled_features: [@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool = undefined;2280 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
2281 try wasm.validateFeatures(arena, &enabled_features, &emit_features_count);2281 try wasm.validateFeatures(arena, &enabled_features, &emit_features_count);
2282 try wasm.resolveSymbolsInArchives();2282 try wasm.resolveSymbolsInArchives();
2283 try wasm.checkUndefinedSymbols();2283 try wasm.checkUndefinedSymbols();
src/link/Wasm/types.zig+23-1
...@@ -183,7 +183,27 @@ pub const Feature = struct {...@@ -183,7 +183,27 @@ pub const Feature = struct {
183 /// Type of the feature, must be unique in the sequence of features.183 /// Type of the feature, must be unique in the sequence of features.
184 tag: Tag,184 tag: Tag,
185185
186 pub const Tag = std.Target.wasm.Feature;186 /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
187 pub const Tag = enum {
188 atomics,
189 bulk_memory,
190 exception_handling,
191 extended_const,
192 multivalue,
193 mutable_globals,
194 nontrapping_fptoint,
195 reference_types,
196 relaxed_simd,
197 sign_ext,
198 simd128,
199 tail_call,
200 shared_mem,
201
202 /// From a given cpu feature, returns its linker feature
203 pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
204 return @intToEnum(Tag, @enumToInt(feature));
205 }
206 };
187207
188 pub const Prefix = enum(u8) {208 pub const Prefix = enum(u8) {
189 used = '+',209 used = '+',
...@@ -205,6 +225,7 @@ pub const Feature = struct {...@@ -205,6 +225,7 @@ pub const Feature = struct {
205 .sign_ext => "sign-ext",225 .sign_ext => "sign-ext",
206 .simd128 => "simd128",226 .simd128 => "simd128",
207 .tail_call => "tail-call",227 .tail_call => "tail-call",
228 .shared_mem => "shared-mem",
208 };229 };
209 }230 }
210231
...@@ -228,4 +249,5 @@ pub const known_features = std.ComptimeStringMap(Feature.Tag, .{...@@ -228,4 +249,5 @@ pub const known_features = std.ComptimeStringMap(Feature.Tag, .{
228 .{ "sign-ext", .sign_ext },249 .{ "sign-ext", .sign_ext },
229 .{ "simd128", .simd128 },250 .{ "simd128", .simd128 },
230 .{ "tail-call", .tail_call },251 .{ "tail-call", .tail_call },
252 .{ "shared-mem", .shared_mem },
231});253});