Commit 3d1d19f387

Luuk de Gram <luuk@degram.dev>
2022-10-23 20:02:25
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.
1 parent 2f41109
Changed files (2)
src
src/link/Wasm/types.zig
@@ -183,7 +183,27 @@ pub const Feature = struct {
     /// Type of the feature, must be unique in the sequence of features.
     tag: Tag,
 
-    pub const Tag = std.Target.wasm.Feature;
+    /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem
+    pub const Tag = enum {
+        atomics,
+        bulk_memory,
+        exception_handling,
+        extended_const,
+        multivalue,
+        mutable_globals,
+        nontrapping_fptoint,
+        reference_types,
+        relaxed_simd,
+        sign_ext,
+        simd128,
+        tail_call,
+        shared_mem,
+
+        /// From a given cpu feature, returns its linker feature
+        pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
+            return @intToEnum(Tag, @enumToInt(feature));
+        }
+    };
 
     pub const Prefix = enum(u8) {
         used = '+',
@@ -205,6 +225,7 @@ pub const Feature = struct {
             .sign_ext => "sign-ext",
             .simd128 => "simd128",
             .tail_call => "tail-call",
+            .shared_mem => "shared-mem",
         };
     }
 
@@ -228,4 +249,5 @@ pub const known_features = std.ComptimeStringMap(Feature.Tag, .{
     .{ "sign-ext", .sign_ext },
     .{ "simd128", .simd128 },
     .{ "tail-call", .tail_call },
+    .{ "shared-mem", .shared_mem },
 });
src/link/Wasm.zig
@@ -654,15 +654,15 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
 fn validateFeatures(
     wasm: *const Wasm,
     arena: Allocator,
-    to_emit: *[@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool,
+    to_emit: *[@typeInfo(types.Feature.Tag).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);
-    var used = std.AutoArrayHashMap(std.Target.wasm.Feature, []const u8).init(arena);
-    var disallowed = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena);
-    var required = std.AutoHashMap(std.Target.wasm.Feature, []const u8).init(arena);
+    var allowed = std.AutoHashMap(types.Feature.Tag, void).init(arena);
+    var used = std.AutoArrayHashMap(types.Feature.Tag, []const u8).init(arena);
+    var disallowed = std.AutoHashMap(types.Feature.Tag, []const u8).init(arena);
+    var required = std.AutoHashMap(types.Feature.Tag, []const u8).init(arena);
 
     // when false, we fail linking. We only verify this after a loop to catch all invalid features.
     var valid_feature_set = true;
@@ -674,7 +674,7 @@ fn validateFeatures(
         // std.builtin.Type.EnumField
         inline for (@typeInfo(std.Target.wasm.Feature).Enum.fields) |feature_field| {
             if (cpu_features.isEnabled(feature_field.value)) {
-                allowed.putAssumeCapacityNoClobber(@intToEnum(std.Target.wasm.Feature, feature_field.value), {});
+                allowed.putAssumeCapacityNoClobber(@intToEnum(types.Feature.Tag, feature_field.value), {});
             }
         }
     }
@@ -730,7 +730,7 @@ fn validateFeatures(
 
     // For each linked object, validate the required and disallowed features
     for (wasm.objects.items) |object| {
-        var object_used_features = std.AutoHashMap(std.Target.wasm.Feature, void).init(arena);
+        var object_used_features = std.AutoHashMap(types.Feature.Tag, void).init(arena);
         try object_used_features.ensureTotalCapacity(@intCast(u32, object.features.len));
         for (object.features) |feature| {
             if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
@@ -764,7 +764,7 @@ fn validateFeatures(
     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));
+            feature_enabled.* = allowed.contains(@intToEnum(types.Feature.Tag, feature_index));
         }
     }
 }
@@ -2277,7 +2277,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
     }
 
     var emit_features_count: u32 = 0;
-    var enabled_features: [@typeInfo(std.Target.wasm.Feature).Enum.fields.len]bool = undefined;
+    var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
     try wasm.validateFeatures(arena, &enabled_features, &emit_features_count);
     try wasm.resolveSymbolsInArchives();
     try wasm.checkUndefinedSymbols();