authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-15 19:05:49+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-16 15:54:17+02:00
log273b8e20ca086b62debc869e1c375cd834043e24
tree3ff23c1b26e81ed5a63bfb60ffb9ea9bd306a219
parent6fcd72355cac1495c213da20a6cf4f6f30bd2a65
signaturelock-open Commit is signed but in an unrecognized format.

wasm: allow merging single branches

Rather than accepting a canonical branch and a target branch we allow to directly merge a branch into the parent branch. This is possible as there's no overlapping and we have infinite registers to our availability. This makes merging a lot simpler.

1 files changed, 42 insertions(+), 46 deletions(-)

src/arch/wasm/CodeGen.zig+42-46
...@@ -810,7 +810,7 @@ const BigTomb = struct {...@@ -810,7 +810,7 @@ const BigTomb = struct {
810};810};
811811
812fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {812fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
813 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, operand_count + 1));813 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, operand_count + 1);
814 return BigTomb{814 return BigTomb{
815 .gen = self,815 .gen = self,
816 .inst = inst,816 .inst = inst,
...@@ -826,7 +826,7 @@ fn processDeath(self: *Self, ref: Air.Inst.Ref) void {...@@ -826,7 +826,7 @@ fn processDeath(self: *Self, ref: Air.Inst.Ref) void {
826 // TODO: Upon branch consolidation free any locals if needed.826 // TODO: Upon branch consolidation free any locals if needed.
827 const value = self.currentBranch().values.getPtr(ref) orelse return;827 const value = self.currentBranch().values.getPtr(ref) orelse return;
828 if (value.* != .local) return;828 if (value.* != .local) return;
829 std.debug.print("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)});829 log.debug("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)});
830 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer830 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
831 if (value.local.references == 0) {831 if (value.local.references == 0) {
832 value.free(self);832 value.free(self);
...@@ -989,18 +989,23 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {...@@ -989,18 +989,23 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
989 const valtype = typeToValtype(ty, self.target);989 const valtype = typeToValtype(ty, self.target);
990 switch (valtype) {990 switch (valtype) {
991 .i32 => if (self.free_locals_i32.popOrNull()) |index| {991 .i32 => if (self.free_locals_i32.popOrNull()) |index| {
992 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
992 return WValue{ .local = .{ .value = index, .references = 1 } };993 return WValue{ .local = .{ .value = index, .references = 1 } };
993 },994 },
994 .i64 => if (self.free_locals_i64.popOrNull()) |index| {995 .i64 => if (self.free_locals_i64.popOrNull()) |index| {
996 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
995 return WValue{ .local = .{ .value = index, .references = 1 } };997 return WValue{ .local = .{ .value = index, .references = 1 } };
996 },998 },
997 .f32 => if (self.free_locals_f32.popOrNull()) |index| {999 .f32 => if (self.free_locals_f32.popOrNull()) |index| {
1000 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
998 return WValue{ .local = .{ .value = index, .references = 1 } };1001 return WValue{ .local = .{ .value = index, .references = 1 } };
999 },1002 },
1000 .f64 => if (self.free_locals_f64.popOrNull()) |index| {1003 .f64 => if (self.free_locals_f64.popOrNull()) |index| {
1004 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1001 return WValue{ .local = .{ .value = index, .references = 1 } };1005 return WValue{ .local = .{ .value = index, .references = 1 } };
1002 },1006 },
1003 }1007 }
1008 log.debug("new local of type {}\n", .{valtype});
1004 // no local was free to be re-used, so allocate a new local instead1009 // no local was free to be re-used, so allocate a new local instead
1005 return self.ensureAllocLocal(ty);1010 return self.ensureAllocLocal(ty);
1006}1011}
...@@ -1888,7 +1893,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -1888,7 +1893,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {
1888fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {1893fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1889 for (body) |inst| {1894 for (body) |inst| {
1890 const old_bookkeeping_value = self.air_bookkeeping;1895 const old_bookkeeping_value = self.air_bookkeeping;
1891 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, Liveness.bpi);1896 // TODO: Determine why we need to pre-allocate an extra 4 possible values here.
1897 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, Liveness.bpi + 4);
1892 try self.genInst(inst);1898 try self.genInst(inst);
18931899
1894 if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) {1900 if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) {
...@@ -1897,10 +1903,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1897,10 +1903,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1897 self.air.instructions.items(.tag)[inst],1903 self.air.instructions.items(.tag)[inst],
1898 });1904 });
1899 }1905 }
1900 // if (result != .none) {
1901 // assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack
1902 // try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);
1903 // }
1904 }1906 }
1905}1907}
19061908
...@@ -2844,65 +2846,43 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -2844,65 +2846,43 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
28442846
2845 try self.branches.ensureUnusedCapacity(self.gpa, 2);2847 try self.branches.ensureUnusedCapacity(self.gpa, 2);
28462848
2847 const else_stack = self.branches.addOneAssumeCapacity();2849 self.branches.appendAssumeCapacity(.{});
2848 else_stack.* = .{};
2849 defer else_stack.deinit(self.gpa);
2850
2851 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.else_deaths.len));2850 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
2852 for (liveness_condbr.else_deaths) |death| {2851 for (liveness_condbr.else_deaths) |death| {
2853 self.processDeath(Air.indexToRef(death));2852 self.processDeath(Air.indexToRef(death));
2854 std.debug.print("Death inst: %{d}\n", .{death});
2855 }2853 }
2856 try self.genBody(else_body);2854 try self.genBody(else_body);
2857 try self.endBlock();2855 try self.endBlock();
2858 else_stack.* = self.branches.pop();2856 var else_stack = self.branches.pop();
2857 defer else_stack.deinit(self.gpa);
28592858
2860 // Outer block that matches the condition2859 // Outer block that matches the condition
2861 const then_stack = self.branches.addOneAssumeCapacity();2860 self.branches.appendAssumeCapacity(.{});
2862 then_stack.* = .{};
2863 defer then_stack.deinit(self.gpa);
2864
2865 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.then_deaths.len));2861 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
2866 for (liveness_condbr.then_deaths) |death| {2862 for (liveness_condbr.then_deaths) |death| {
2867 self.processDeath(Air.indexToRef(death));2863 self.processDeath(Air.indexToRef(death));
2868 std.debug.print("Death inst: %{d}\n", .{death});
2869 }2864 }
2870 try self.genBody(then_body);2865 try self.genBody(then_body);
2871 then_stack.* = self.branches.pop();2866 var then_stack = self.branches.pop();
2867 defer then_stack.deinit(self.gpa);
28722868
2873 try self.canonicaliseBranches(then_stack, else_stack);2869 try self.mergeBranch(&else_stack);
2870 try self.mergeBranch(&then_stack);
28742871
2875 // TODO: Branch consilidation to process deaths from branches
2876 self.finishAir(inst, .none, &.{});2872 self.finishAir(inst, .none, &.{});
2877}2873}
28782874
2879fn canonicaliseBranches(self: *Self, canon_branch: *Branch, target_branch: *Branch) !void {2875fn mergeBranch(self: *Self, branch: *const Branch) !void {
2880 const parent = self.currentBranch();2876 const parent = self.currentBranch();
28812877
2882 const target_slice = target_branch.values.entries.slice();2878 const target_slice = branch.values.entries.slice();
2883 const target_keys = target_slice.items(.key);2879 const target_keys = target_slice.items(.key);
2884 const target_values = target_slice.items(.value);2880 const target_values = target_slice.items(.value);
28852881
2886 try parent.values.ensureUnusedCapacity(self.gpa, target_branch.values.count());2882 try parent.values.ensureUnusedCapacity(self.gpa, branch.values.count());
2887 for (target_keys) |key, index| {2883 for (target_keys) |key, index| {
2888 const value = target_values[index];2884 // TODO: process deaths from branches
2889 const canon_value = if (canon_branch.values.fetchSwapRemove(key)) |canon_entry| {2885 parent.values.putAssumeCapacity(key, target_values[index]);
2890 // try parent.values.putAssumeCapacity(key, canon_entry.value);
2891 _ = canon_entry;
2892 // _ = result_value;
2893 @panic("HMMMM THIS occurs");
2894 // break :result_value canon_entry.value;
2895 } else value;
2896
2897 parent.values.putAssumeCapacity(key, canon_value);
2898 }
2899
2900 try parent.values.ensureUnusedCapacity(self.gpa, canon_branch.values.count());
2901 const canon_slice = canon_branch.values.entries.slice();
2902 const canon_keys = canon_slice.items(.key);
2903 const canon_values = canon_slice.items(.value);
2904 for (canon_keys) |key, index| {
2905 parent.values.putAssumeCapacity(key, canon_values[index]);
2906 }2886 }
2907}2887}
29082888
...@@ -3173,6 +3153,9 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -3173,6 +3153,9 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3173 const target = try self.resolveInst(pl_op.operand);3153 const target = try self.resolveInst(pl_op.operand);
3174 const target_ty = self.air.typeOf(pl_op.operand);3154 const target_ty = self.air.typeOf(pl_op.operand);
3175 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);3155 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
3156 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
3157 defer self.gpa.free(liveness.deaths);
3158
3176 var extra_index: usize = switch_br.end;3159 var extra_index: usize = switch_br.end;
3177 var case_i: u32 = 0;3160 var case_i: u32 = 0;
31783161
...@@ -3283,7 +3266,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -3283,7 +3266,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3283 };3266 };
32843267
3285 try self.branches.ensureUnusedCapacity(self.gpa, case_list.items.len + @boolToInt(has_else_body));3268 try self.branches.ensureUnusedCapacity(self.gpa, case_list.items.len + @boolToInt(has_else_body));
3286 for (case_list.items) |case| {3269 for (case_list.items) |case, index| {
3287 // when sparse, we use if/else-chain, so emit conditional checks3270 // when sparse, we use if/else-chain, so emit conditional checks
3288 if (is_sparse) {3271 if (is_sparse) {
3289 // for single value prong we can emit a simple if3272 // for single value prong we can emit a simple if
...@@ -3318,18 +3301,31 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {...@@ -3318,18 +3301,31 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3318 try self.endBlock();3301 try self.endBlock();
3319 }3302 }
3320 }3303 }
3321 // try self.branches.items
3322 self.branches.appendAssumeCapacity(.{});3304 self.branches.appendAssumeCapacity(.{});
3305
3306 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, liveness.deaths[index].len);
3307 for (liveness.deaths[index]) |operand| {
3308 self.processDeath(Air.indexToRef(operand));
3309 }
3323 try self.genBody(case.body);3310 try self.genBody(case.body);
3324 try self.endBlock();3311 try self.endBlock();
3325 _ = self.branches.pop();3312 var case_branch = self.branches.pop();
3313 defer case_branch.deinit(self.gpa);
3314 try self.mergeBranch(&case_branch);
3326 }3315 }
33273316
3328 if (has_else_body) {3317 if (has_else_body) {
3329 self.branches.appendAssumeCapacity(.{});3318 self.branches.appendAssumeCapacity(.{});
3319 const else_deaths = liveness.deaths.len - 1;
3320 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, liveness.deaths[else_deaths].len);
3321 for (liveness.deaths[else_deaths]) |operand| {
3322 self.processDeath(Air.indexToRef(operand));
3323 }
3330 try self.genBody(else_body);3324 try self.genBody(else_body);
3331 try self.endBlock();3325 try self.endBlock();
3332 _ = self.branches.pop();3326 var else_branch = self.branches.pop();
3327 defer else_branch.deinit(self.gpa);
3328 try self.mergeBranch(&else_branch);
3333 }3329 }
3334 self.finishAir(inst, .none, &.{});3330 self.finishAir(inst, .none, &.{});
3335}3331}