authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-08-21 00:44:06+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-09-17 00:04:51+02:00
log5ec1668eb4cee3ce3069f6ff7b2e3af0d52f43a6
tree3bf79bb99c9056b7ca4c0175d2fd4d5b4d01886e
parent39512c3e5547b411294369c8eec8093045f74d64

wasm: remove reuseOperand


1 files changed, 41 insertions(+), 59 deletions(-)

src/codegen/wasm/CodeGen.zig+41-59
......@@ -421,6 +421,20 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {
421421/// NOTE: if result == .stack, it will be stored in .local
422422fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {
423423 assert(operands.len <= Air.Liveness.bpi - 1);
424
425 var final_result = result;
426 const aliased_operand: ?*WValue = if (result == .local) aliased: {
427 for (operands) |operand| {
428 if (operand.toIndex() == null) continue;
429 const resolved_operand = cg.getResolvedInst(operand);
430 if (resolved_operand.* != .local or resolved_operand.local.value != result.local.value) continue;
431
432 resolved_operand.local.references += 1;
433 break :aliased resolved_operand;
434 }
435 break :aliased null;
436 } else null;
437
424438 var tomb_bits = cg.liveness.getTombBits(inst);
425439 for (operands) |operand| {
426440 const dies = @as(u1, @truncate(tomb_bits)) != 0;
......@@ -428,7 +442,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con
428442 if (!dies) continue;
429443 processDeath(cg, operand);
430444 }
431 try cg.finishAirResult(inst, result);
445 if (aliased_operand) |operand| final_result.local.references = operand.local.references;
446 try cg.finishAirResult(inst, final_result);
432447}
433448
434449fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void {
......@@ -478,7 +493,7 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
478493 return; // function arguments can never be re-used
479494 }
480495 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @backingInt(ref.toIndex().?), value.local.value });
481 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
496 value.local.references -= 1;
482497 if (value.local.references == 0) {
483498 value.free(cg);
484499 }
......@@ -631,7 +646,7 @@ fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.B
631646/// Writes the bytecode depending on the given `WValue` in `val`
632647fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
633648 switch (value) {
634 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
649 .dead => unreachable, // reference to free'd `WValue`
635650 .none, .stack => {}, // no-op
636651 .local => |idx| try cg.addLocal(.local_get, idx.value),
637652 .imm32 => |val| try cg.addImm32(val),
......@@ -699,23 +714,6 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
699714 }
700715}
701716
702/// If given a local or stack-offset, increases the reference count by 1.
703/// The old `WValue` found at instruction `ref` is then replaced by the
704/// modified `WValue` and returned. When given a non-local or non-stack-offset,
705/// returns the given `operand` itfunc instead.
706fn reuseOperand(cg: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
707 if (operand != .local and operand != .stack_offset) return operand;
708 var new_value = operand;
709 switch (new_value) {
710 .local => |*local| local.references += 1,
711 .stack_offset => |*stack_offset| stack_offset.references += 1,
712 else => unreachable,
713 }
714 const old_value = cg.getResolvedInst(ref);
715 old_value.* = new_value;
716 return new_value;
717}
718
719717/// From a reference, returns its resolved `WValue`.
720718/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
721719fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
......@@ -1598,17 +1596,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
15981596 const src_int_ty: IntType = .fromType(cg, src_ty);
15991597 const dest_int_ty: IntType = .fromType(cg, dest_ty);
16001598
1601 const src_bits = src_int_ty.bits;
1602 const dest_bits = dest_int_ty.bits;
1603
1604 const same_class: bool = (src_bits <= 32 and dest_bits <= 32) or
1605 (src_bits >= 33 and src_bits <= 64 and dest_bits >= 33 and dest_bits <= 64) or
1606 (src_bits >= 65 and src_bits <= 128 and dest_bits >= 65 and dest_bits <= 128);
1607
1608 const result = if (same_class)
1609 cg.reuseOperand(ty_op.operand, operand)
1610 else
1611 try cg.intCast(dest_int_ty, src_int_ty, operand);
1599 const result = try cg.intCast(dest_int_ty, src_int_ty, operand);
16121600
16131601 try cg.finishAir(inst, result, &.{ty_op.operand});
16141602 },
......@@ -1625,12 +1613,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
16251613
16261614 const src_int_ty: IntType = .fromType(cg, src_ty);
16271615 const dest_int_ty: IntType = .fromType(cg, dest_ty);
1628
1629 const result = if (src_int_ty.bits == dest_int_ty.bits)
1630 cg.reuseOperand(ty_op.operand, operand)
1631 else blk: {
1632 break :blk try cg.intTrunc(dest_int_ty, src_int_ty, operand);
1633 };
1616 const result = try cg.intTrunc(dest_int_ty, src_int_ty, operand);
16341617
16351618 try cg.finishAir(inst, result, &.{ty_op.operand});
16361619 },
......@@ -4259,6 +4242,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
42594242}
42604243
42614244fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
4245 assert(src_ty.bits > dest_ty.bits);
42624246 var result = try cg.intCast(dest_ty, src_ty, operand);
42634247
42644248 const dest_wasm_bits = cg.intBackingBits(dest_ty.bits);
......@@ -5584,8 +5568,7 @@ fn airNopCast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55845568 assert(operand_ty.abiAlignment(zcu) == dest_ty.abiAlignment(zcu));
55855569
55865570 const operand = try cg.resolveInst(ty_op.operand);
5587 const result = cg.reuseOperand(ty_op.operand, operand);
5588 return cg.finishAir(inst, result, &.{ty_op.operand});
5571 return cg.finishAir(inst, operand, &.{ty_op.operand});
55895572}
55905573
55915574fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5601,7 +5584,7 @@ fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56015584 const operand = try cg.resolveInst(ty_op.operand);
56025585 const result = switch (operand) {
56035586 .stack_offset => try cg.buildPointerOffset(operand, 0, .new),
5604 else => cg.reuseOperand(ty_op.operand, operand),
5587 else => operand,
56055588 };
56065589 return cg.finishAir(inst, result, &.{ty_op.operand});
56075590}
......@@ -5627,7 +5610,7 @@ fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56275610 const dest_ty = cg.typeOfIndex(inst);
56285611 const src_ty = cg.typeOf(ty_op.operand);
56295612
5630 const result = (try cg.bitcast(dest_ty, src_ty, operand)) orelse cg.reuseOperand(ty_op.operand, operand);
5613 const result = try cg.bitcast(dest_ty, src_ty, operand);
56315614
56325615 return cg.finishAir(inst, result, &.{ty_op.operand});
56335616}
......@@ -5654,8 +5637,8 @@ fn bitcastClass(cg: *CodeGen, ty: Type) BitcastClass {
56545637 };
56555638}
56565639
5657fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!?WValue {
5658 if (dest_ty.eql(src_ty)) return null;
5640fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!WValue {
5641 if (dest_ty.eql(src_ty)) return operand;
56595642
56605643 const zcu = cg.pt.zcu;
56615644 const src_class = cg.bitcastClass(src_ty);
......@@ -5674,7 +5657,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
56745657
56755658 if (src_by_ref and dest_by_ref) {
56765659 if (needs_wrapping) return try cg.intWrap(dest_class.int, operand);
5677 return null;
5660 return operand;
56785661 }
56795662
56805663 if (dest_by_ref) {
......@@ -5708,7 +5691,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
57085691
57095692 switch (dest_class) {
57105693 .float => |float_ty| switch (float_ty) {
5711 .f16 => return null,
5694 .f16 => return operand,
57125695 .f32 => {
57135696 try cg.emitWValue(operand);
57145697 try cg.addTag(.f32_reinterpret_i32);
......@@ -5729,7 +5712,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
57295712 return try cg.intWrap(dest_class.int, operand);
57305713 }
57315714
5732 return null;
5715 return operand;
57335716}
57345717
57355718fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5740,7 +5723,7 @@ fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57405723 const struct_ptr = try cg.resolveInst(extra.data.struct_operand);
57415724 const struct_ptr_ty = cg.typeOf(extra.data.struct_operand);
57425725 const struct_ty = struct_ptr_ty.childType(zcu);
5743 const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
5726 const result = try cg.structFieldPtr(inst, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
57445727 return cg.finishAir(inst, result, &.{extra.data.struct_operand});
57455728}
57465729
......@@ -5751,14 +5734,13 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerE
57515734 const struct_ptr_ty = cg.typeOf(ty_op.operand);
57525735 const struct_ty = struct_ptr_ty.childType(zcu);
57535736
5754 const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index);
5737 const result = try cg.structFieldPtr(inst, struct_ptr, struct_ptr_ty, struct_ty, index);
57555738 return cg.finishAir(inst, result, &.{ty_op.operand});
57565739}
57575740
57585741fn structFieldPtr(
57595742 cg: *CodeGen,
57605743 inst: Air.Inst.Index,
5761 ref: Air.Inst.Ref,
57625744 struct_ptr: WValue,
57635745 struct_ptr_ty: Type,
57645746 struct_ty: Type,
......@@ -5785,7 +5767,7 @@ fn structFieldPtr(
57855767 };
57865768 // save a load and store when we can simply reuse the operand
57875769 if (offset == 0) {
5788 return cg.reuseOperand(ref, struct_ptr);
5770 return struct_ptr;
57895771 }
57905772 switch (struct_ptr) {
57915773 .stack_offset => |stack_offset| {
......@@ -6092,7 +6074,7 @@ fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
60926074 const result: WValue = result: {
60936075 if (!payload_ty.hasRuntimeBits(zcu)) {
60946076 if (op_is_ptr) {
6095 break :result cg.reuseOperand(ty_op.operand, operand);
6077 break :result operand;
60966078 } else {
60976079 break :result .none;
60986080 }
......@@ -6131,7 +6113,7 @@ fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) I
61316113 break :result try cg.load(operand, Type.anyerror, err_offset);
61326114 } else {
61336115 assert(!payload_ty.hasRuntimeBits(zcu));
6134 break :result cg.reuseOperand(ty_op.operand, operand);
6116 break :result operand;
61356117 }
61366118 };
61376119 return cg.finishAir(inst, result, &.{ty_op.operand});
......@@ -6147,7 +6129,7 @@ fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61476129 const pl_ty = cg.typeOf(ty_op.operand);
61486130 const result = result: {
61496131 if (!pl_ty.hasRuntimeBits(zcu)) {
6150 break :result cg.reuseOperand(ty_op.operand, operand);
6132 break :result operand;
61516133 }
61526134
61536135 const err_union = try cg.allocStack(err_ty);
......@@ -6177,7 +6159,7 @@ fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61776159
61786160 const result = result: {
61796161 if (!pl_ty.hasRuntimeBits(zcu)) {
6180 break :result cg.reuseOperand(ty_op.operand, operand);
6162 break :result operand;
61816163 }
61826164
61836165 const err_union = try cg.allocStack(err_ty);
......@@ -6257,7 +6239,7 @@ fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62576239
62586240 const result = result: {
62596241 const operand = try cg.resolveInst(ty_op.operand);
6260 if (opt_ty.optionalReprIsPayload(zcu)) break :result cg.reuseOperand(ty_op.operand, operand);
6242 if (opt_ty.optionalReprIsPayload(zcu)) break :result operand;
62616243
62626244 if (isByRef(payload_ty, zcu, cg.target)) {
62636245 break :result try cg.buildPointerOffset(operand, 0, .new);
......@@ -6277,7 +6259,7 @@ fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62776259 const result = result: {
62786260 const payload_ty = opt_ty.optionalChild(zcu);
62796261 if (!payload_ty.hasRuntimeBits(zcu) or opt_ty.optionalReprIsPayload(zcu)) {
6280 break :result cg.reuseOperand(ty_op.operand, operand);
6262 break :result operand;
62816263 }
62826264
62836265 break :result try cg.buildPointerOffset(operand, 0, .new);
......@@ -6327,7 +6309,7 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63276309 const operand = try cg.resolveInst(ty_op.operand);
63286310 const op_ty = cg.typeOfIndex(inst);
63296311 if (op_ty.optionalReprIsPayload(zcu)) {
6330 break :result cg.reuseOperand(ty_op.operand, operand);
6312 break :result operand;
63316313 }
63326314 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
63336315 return cg.fail("Optional type {f} too big to fit into stack frame", .{op_ty.fmt(pt)});
......@@ -7146,7 +7128,7 @@ fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void
71467128
71477129 const result = result: {
71487130 if (!payload_ty.hasRuntimeBits(zcu)) {
7149 break :result cg.reuseOperand(ty_op.operand, operand);
7131 break :result operand;
71507132 }
71517133
71527134 break :result try cg.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new);
......@@ -7182,7 +7164,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71827164 try cg.addTag(.i32_sub);
71837165 try cg.addLocal(.local_set, base.local.value);
71847166 break :result base;
7185 } else cg.reuseOperand(extra.field_ptr, field_ptr);
7167 } else field_ptr;
71867168
71877169 return cg.finishAir(inst, result, &.{extra.field_ptr});
71887170}