diff options
| author | Luuk de Gram <luuk@degram.dev> | 2022-10-13 21:33:26 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-10-16 15:54:16 +0200 |
| commit | 576bb3f0a965cd7ff3dad6076567657f18d6675e (patch) | |
| tree | e6db50bc8e680c73935f3f392b754ed4c8a4e164 | |
| parent | b17c8c542420e14e24ec397b248dfc101a08421e (diff) | |
| download | zig-576bb3f0a965cd7ff3dad6076567657f18d6675e.tar.gz zig-576bb3f0a965cd7ff3dad6076567657f18d6675e.zip | |
wasm: de -and increment reference count locals
When reusing an operand it increases the reference count, then when
an operand dies it will only decrease the reference count. When
this reaches 0, the local will be virtually freed, meaning it can be
re-used for a new local.
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 33 |
1 files changed, 29 insertions, 4 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b2dedd6d56..7543885fe9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -789,8 +789,13 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT fn processDeath(self: *Self, ref: Air.Inst.Ref) void { const inst = Air.refToIndex(ref) orelse return; if (self.air.instructions.items(.tag)[inst] == .constant) return; - var value = self.values.get(ref) orelse return; - value.free(self); + const value = self.values.getPtr(ref) orelse return; + if (value.* != .local) return; + std.debug.print("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)}); + value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer + if (value.local.references == 0) { + value.free(self); + } } /// Appends a MIR instruction and returns its index within the list of instructions @@ -909,10 +914,29 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); }, .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation - .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local), // caller must ensure to address the offset + .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local.value), // caller must ensure to address the offset } } +/// If given a local or stack-offset, increases the reference count by 1. +/// The old `WValue` found at instruction `ref` is then replaced by the +/// modified `WValue` and returned. When given a non-local or non-stack-offset, +/// returns the given `operand` itself instead. +fn reuseOperand(self: *Self, ref: Air.Inst.Ref, operand: WValue) WValue { + if (operand != .local and operand != .stack_offset) return operand; + var copy = operand; + switch (copy) { + .local => |*local| local.references += 1, + .stack_offset => |*stack_offset| stack_offset.references += 1, + else => unreachable, + } + + const gop = self.values.getOrPutAssumeCapacity(ref); + assert(gop.found_existing); + gop.value_ptr.* = copy; + return copy; +} + /// Creates one locals for a given `Type`. /// Returns a corresponding `Wvalue` with `local` as active tag fn allocLocal(self: *Self, ty: Type) InnerError!WValue { @@ -2956,7 +2980,8 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!void { fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result = if (!self.liveness.isUnused(inst)) result: { - break :result try self.resolveInst(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + break :result self.reuseOperand(ty_op.operand, operand); } else WValue{ .none = {} }; self.finishAir(inst, result, &.{}); } |
