| ... | ... | @@ -421,6 +421,20 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue { |
| 421 | 421 | /// NOTE: if result == .stack, it will be stored in .local |
| 422 | 422 | fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void { |
| 423 | 423 | 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 | |
| 424 | 438 | var tomb_bits = cg.liveness.getTombBits(inst); |
| 425 | 439 | for (operands) |operand| { |
| 426 | 440 | const dies = @as(u1, @truncate(tomb_bits)) != 0; |
| ... | ... | @@ -428,7 +442,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con |
| 428 | 442 | if (!dies) continue; |
| 429 | 443 | processDeath(cg, operand); |
| 430 | 444 | } |
| 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); |
| 432 | 447 | } |
| 433 | 448 | |
| 434 | 449 | fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void { |
| ... | ... | @@ -478,7 +493,7 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { |
| 478 | 493 | return; // function arguments can never be re-used |
| 479 | 494 | } |
| 480 | 495 | 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; |
| 482 | 497 | if (value.local.references == 0) { |
| 483 | 498 | value.free(cg); |
| 484 | 499 | } |
| ... | ... | @@ -631,7 +646,7 @@ fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.B |
| 631 | 646 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 632 | 647 | fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 633 | 648 | switch (value) { |
| 634 | | .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?) |
| 649 | .dead => unreachable, // reference to free'd `WValue` |
| 635 | 650 | .none, .stack => {}, // no-op |
| 636 | 651 | .local => |idx| try cg.addLocal(.local_get, idx.value), |
| 637 | 652 | .imm32 => |val| try cg.addImm32(val), |
| ... | ... | @@ -699,23 +714,6 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 699 | 714 | } |
| 700 | 715 | } |
| 701 | 716 | |
| 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. |
| 706 | | fn 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 | | |
| 719 | 717 | /// From a reference, returns its resolved `WValue`. |
| 720 | 718 | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. |
| 721 | 719 | fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| ... | ... | @@ -1598,17 +1596,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1598 | 1596 | const src_int_ty: IntType = .fromType(cg, src_ty); |
| 1599 | 1597 | const dest_int_ty: IntType = .fromType(cg, dest_ty); |
| 1600 | 1598 | |
| 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); |
| 1612 | 1600 | |
| 1613 | 1601 | try cg.finishAir(inst, result, &.{ty_op.operand}); |
| 1614 | 1602 | }, |
| ... | ... | @@ -1625,12 +1613,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1625 | 1613 | |
| 1626 | 1614 | const src_int_ty: IntType = .fromType(cg, src_ty); |
| 1627 | 1615 | 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); |
| 1634 | 1617 | |
| 1635 | 1618 | try cg.finishAir(inst, result, &.{ty_op.operand}); |
| 1636 | 1619 | }, |
| ... | ... | @@ -4259,6 +4242,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn |
| 4259 | 4242 | } |
| 4260 | 4243 | |
| 4261 | 4244 | fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 4245 | assert(src_ty.bits > dest_ty.bits); |
| 4262 | 4246 | var result = try cg.intCast(dest_ty, src_ty, operand); |
| 4263 | 4247 | |
| 4264 | 4248 | const dest_wasm_bits = cg.intBackingBits(dest_ty.bits); |
| ... | ... | @@ -5584,8 +5568,7 @@ fn airNopCast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5584 | 5568 | assert(operand_ty.abiAlignment(zcu) == dest_ty.abiAlignment(zcu)); |
| 5585 | 5569 | |
| 5586 | 5570 | 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}); |
| 5589 | 5572 | } |
| 5590 | 5573 | |
| 5591 | 5574 | fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5601,7 +5584,7 @@ fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5601 | 5584 | const operand = try cg.resolveInst(ty_op.operand); |
| 5602 | 5585 | const result = switch (operand) { |
| 5603 | 5586 | .stack_offset => try cg.buildPointerOffset(operand, 0, .new), |
| 5604 | | else => cg.reuseOperand(ty_op.operand, operand), |
| 5587 | else => operand, |
| 5605 | 5588 | }; |
| 5606 | 5589 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5607 | 5590 | } |
| ... | ... | @@ -5627,7 +5610,7 @@ fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5627 | 5610 | const dest_ty = cg.typeOfIndex(inst); |
| 5628 | 5611 | const src_ty = cg.typeOf(ty_op.operand); |
| 5629 | 5612 | |
| 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); |
| 5631 | 5614 | |
| 5632 | 5615 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5633 | 5616 | } |
| ... | ... | @@ -5654,8 +5637,8 @@ fn bitcastClass(cg: *CodeGen, ty: Type) BitcastClass { |
| 5654 | 5637 | }; |
| 5655 | 5638 | } |
| 5656 | 5639 | |
| 5657 | | fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!?WValue { |
| 5658 | | if (dest_ty.eql(src_ty)) return null; |
| 5640 | fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!WValue { |
| 5641 | if (dest_ty.eql(src_ty)) return operand; |
| 5659 | 5642 | |
| 5660 | 5643 | const zcu = cg.pt.zcu; |
| 5661 | 5644 | const src_class = cg.bitcastClass(src_ty); |
| ... | ... | @@ -5674,7 +5657,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro |
| 5674 | 5657 | |
| 5675 | 5658 | if (src_by_ref and dest_by_ref) { |
| 5676 | 5659 | if (needs_wrapping) return try cg.intWrap(dest_class.int, operand); |
| 5677 | | return null; |
| 5660 | return operand; |
| 5678 | 5661 | } |
| 5679 | 5662 | |
| 5680 | 5663 | if (dest_by_ref) { |
| ... | ... | @@ -5708,7 +5691,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro |
| 5708 | 5691 | |
| 5709 | 5692 | switch (dest_class) { |
| 5710 | 5693 | .float => |float_ty| switch (float_ty) { |
| 5711 | | .f16 => return null, |
| 5694 | .f16 => return operand, |
| 5712 | 5695 | .f32 => { |
| 5713 | 5696 | try cg.emitWValue(operand); |
| 5714 | 5697 | try cg.addTag(.f32_reinterpret_i32); |
| ... | ... | @@ -5729,7 +5712,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro |
| 5729 | 5712 | return try cg.intWrap(dest_class.int, operand); |
| 5730 | 5713 | } |
| 5731 | 5714 | |
| 5732 | | return null; |
| 5715 | return operand; |
| 5733 | 5716 | } |
| 5734 | 5717 | |
| 5735 | 5718 | fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5740,7 +5723,7 @@ fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5740 | 5723 | const struct_ptr = try cg.resolveInst(extra.data.struct_operand); |
| 5741 | 5724 | const struct_ptr_ty = cg.typeOf(extra.data.struct_operand); |
| 5742 | 5725 | 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); |
| 5744 | 5727 | return cg.finishAir(inst, result, &.{extra.data.struct_operand}); |
| 5745 | 5728 | } |
| 5746 | 5729 | |
| ... | ... | @@ -5751,14 +5734,13 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerE |
| 5751 | 5734 | const struct_ptr_ty = cg.typeOf(ty_op.operand); |
| 5752 | 5735 | const struct_ty = struct_ptr_ty.childType(zcu); |
| 5753 | 5736 | |
| 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); |
| 5755 | 5738 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| 5756 | 5739 | } |
| 5757 | 5740 | |
| 5758 | 5741 | fn structFieldPtr( |
| 5759 | 5742 | cg: *CodeGen, |
| 5760 | 5743 | inst: Air.Inst.Index, |
| 5761 | | ref: Air.Inst.Ref, |
| 5762 | 5744 | struct_ptr: WValue, |
| 5763 | 5745 | struct_ptr_ty: Type, |
| 5764 | 5746 | struct_ty: Type, |
| ... | ... | @@ -5785,7 +5767,7 @@ fn structFieldPtr( |
| 5785 | 5767 | }; |
| 5786 | 5768 | // save a load and store when we can simply reuse the operand |
| 5787 | 5769 | if (offset == 0) { |
| 5788 | | return cg.reuseOperand(ref, struct_ptr); |
| 5770 | return struct_ptr; |
| 5789 | 5771 | } |
| 5790 | 5772 | switch (struct_ptr) { |
| 5791 | 5773 | .stack_offset => |stack_offset| { |
| ... | ... | @@ -6092,7 +6074,7 @@ fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) |
| 6092 | 6074 | const result: WValue = result: { |
| 6093 | 6075 | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 6094 | 6076 | if (op_is_ptr) { |
| 6095 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6077 | break :result operand; |
| 6096 | 6078 | } else { |
| 6097 | 6079 | break :result .none; |
| 6098 | 6080 | } |
| ... | ... | @@ -6131,7 +6113,7 @@ fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) I |
| 6131 | 6113 | break :result try cg.load(operand, Type.anyerror, err_offset); |
| 6132 | 6114 | } else { |
| 6133 | 6115 | assert(!payload_ty.hasRuntimeBits(zcu)); |
| 6134 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6116 | break :result operand; |
| 6135 | 6117 | } |
| 6136 | 6118 | }; |
| 6137 | 6119 | return cg.finishAir(inst, result, &.{ty_op.operand}); |
| ... | ... | @@ -6147,7 +6129,7 @@ fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6147 | 6129 | const pl_ty = cg.typeOf(ty_op.operand); |
| 6148 | 6130 | const result = result: { |
| 6149 | 6131 | if (!pl_ty.hasRuntimeBits(zcu)) { |
| 6150 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6132 | break :result operand; |
| 6151 | 6133 | } |
| 6152 | 6134 | |
| 6153 | 6135 | const err_union = try cg.allocStack(err_ty); |
| ... | ... | @@ -6177,7 +6159,7 @@ fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6177 | 6159 | |
| 6178 | 6160 | const result = result: { |
| 6179 | 6161 | if (!pl_ty.hasRuntimeBits(zcu)) { |
| 6180 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6162 | break :result operand; |
| 6181 | 6163 | } |
| 6182 | 6164 | |
| 6183 | 6165 | const err_union = try cg.allocStack(err_ty); |
| ... | ... | @@ -6257,7 +6239,7 @@ fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6257 | 6239 | |
| 6258 | 6240 | const result = result: { |
| 6259 | 6241 | 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; |
| 6261 | 6243 | |
| 6262 | 6244 | if (isByRef(payload_ty, zcu, cg.target)) { |
| 6263 | 6245 | break :result try cg.buildPointerOffset(operand, 0, .new); |
| ... | ... | @@ -6277,7 +6259,7 @@ fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6277 | 6259 | const result = result: { |
| 6278 | 6260 | const payload_ty = opt_ty.optionalChild(zcu); |
| 6279 | 6261 | if (!payload_ty.hasRuntimeBits(zcu) or opt_ty.optionalReprIsPayload(zcu)) { |
| 6280 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6262 | break :result operand; |
| 6281 | 6263 | } |
| 6282 | 6264 | |
| 6283 | 6265 | break :result try cg.buildPointerOffset(operand, 0, .new); |
| ... | ... | @@ -6327,7 +6309,7 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6327 | 6309 | const operand = try cg.resolveInst(ty_op.operand); |
| 6328 | 6310 | const op_ty = cg.typeOfIndex(inst); |
| 6329 | 6311 | if (op_ty.optionalReprIsPayload(zcu)) { |
| 6330 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 6312 | break :result operand; |
| 6331 | 6313 | } |
| 6332 | 6314 | const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { |
| 6333 | 6315 | 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 |
| 7146 | 7128 | |
| 7147 | 7129 | const result = result: { |
| 7148 | 7130 | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 7149 | | break :result cg.reuseOperand(ty_op.operand, operand); |
| 7131 | break :result operand; |
| 7150 | 7132 | } |
| 7151 | 7133 | |
| 7152 | 7134 | 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 { |
| 7182 | 7164 | try cg.addTag(.i32_sub); |
| 7183 | 7165 | try cg.addLocal(.local_set, base.local.value); |
| 7184 | 7166 | break :result base; |
| 7185 | | } else cg.reuseOperand(extra.field_ptr, field_ptr); |
| 7167 | } else field_ptr; |
| 7186 | 7168 | |
| 7187 | 7169 | return cg.finishAir(inst, result, &.{extra.field_ptr}); |
| 7188 | 7170 | } |