Commit 557f396f61

Luuk de Gram <luuk@degram.dev>
2022-03-07 21:10:31
wasm: Improve switch implementation
- Implement switching over booleans and pointers. - Fix sparse-detection where the lowest value was never truly set as it started at a non-zero number and the case was > 50. - Fix indexing the jump table by ensuring it starts indexing from 0.
1 parent b936fe0
Changed files (1)
src
arch
src/arch/wasm/CodeGen.zig
@@ -1886,6 +1886,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
             const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
             return @bitCast(i32, kv.value);
         },
+        .Bool => return @intCast(i32, val.toSignedInt()),
+        .Pointer => return @intCast(i32, val.toSignedInt()),
         else => unreachable, // Programmer called this function for an illegal type
     }
 }
@@ -2164,8 +2166,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         self.gpa.free(case.values);
     } else case_list.deinit();
 
-    var lowest: i32 = 0;
-    var highest: i32 = 0;
+    var lowest_maybe: ?i32 = null;
+    var highest_maybe: ?i32 = null;
     while (case_i < switch_br.data.cases_len) : (case_i += 1) {
         const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
         const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
@@ -2177,11 +2179,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         for (items) |ref, i| {
             const item_val = self.air.value(ref).?;
             const int_val = self.valueAsI32(item_val, target_ty);
-            if (int_val < lowest) {
-                lowest = int_val;
+            if (lowest_maybe == null or int_val < lowest_maybe.?) {
+                lowest_maybe = int_val;
             }
-            if (int_val > highest) {
-                highest = int_val;
+            if (highest_maybe == null or int_val > highest_maybe.?) {
+                highest_maybe = int_val;
             }
             values[i] = .{ .integer = int_val, .value = item_val };
         }
@@ -2190,6 +2192,9 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         try self.startBlock(.block, blocktype);
     }
 
+    // When highest and lowest are null, we have no cases and can use a jump table
+    const lowest = lowest_maybe orelse 0;
+    const highest = highest_maybe orelse 0;
     // When the highest and lowest values are seperated by '50',
     // we define it as sparse and use an if/else-chain, rather than a jump table.
     // When the target is an integer size larger than u32, we have no way to use the value
@@ -2215,6 +2220,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
             // we put inside, are atleast 0.
             try self.addImm32(lowest * -1);
             try self.addTag(.i32_add);
+        } else if (lowest > 0) {
+            // make the index start from 0 by substracting the lowest value
+            try self.addImm32(lowest);
+            try self.addTag(.i32_sub);
         }
 
         // Account for default branch so always add '1'
@@ -2223,12 +2232,13 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         const table_extra_index = try self.addExtra(jump_table);
         try self.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
         try self.mir_extra.ensureUnusedCapacity(self.gpa, depth);
-        while (lowest <= highest) : (lowest += 1) {
+        var value = lowest;
+        while (value <= highest) : (value += 1) {
             // idx represents the branch we jump to
             const idx = blk: {
                 for (case_list.items) |case, idx| {
                     for (case.values) |case_value| {
-                        if (case_value.integer == lowest) break :blk @intCast(u32, idx);
+                        if (case_value.integer == value) break :blk @intCast(u32, idx);
                     }
                 }
                 break :blk if (has_else_body) case_i else unreachable;