From 6fc524de4267fce0d381215bfda9c2caca9a4f07 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 16 Apr 2023 04:00:23 +0100 Subject: [PATCH] cbe: integrate new Liveness behaviour --- src/codegen/c.zig | 529 +++++++++++----------------------------------- 1 file changed, 122 insertions(+), 407 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 9a89574b38..d2d594c901 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -78,7 +78,6 @@ const LoopDepth = u16; const Local = struct { cty_idx: CType.Index, alignas: CType.AlignAs, - is_in_clone: bool, pub fn getType(local: Local) LocalType { return .{ .cty_idx = local.cty_idx, .alignas = local.alignas }; @@ -275,16 +274,13 @@ pub const Function = struct { /// All the locals, to be emitted at the top of the function. locals: std.ArrayListUnmanaged(Local) = .{}, /// Which locals are available for reuse, based on Type. - /// Only locals in the last stack entry are available for reuse, - /// other entries will become available on loop exit. free_locals_map: LocalsMap = .{}, - is_in_clone: bool = false, /// Locals which will not be freed by Liveness. This is used after a /// Function body is lowered in order to make `free_locals_map` have /// 100% of the locals within so that it can be used to render the block /// of variable declarations at the top of a function, sorted descending /// by type alignment. - /// The value is whether the alloc is static or not. + /// The value is whether the alloc needs to be emitted in the header. allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{}, /// Needed for memory used by the keys of free_locals_map entries. arena: std.heap.ArenaAllocator, @@ -302,7 +298,7 @@ pub const Function = struct { const alignment = 0; const decl_c_value = try f.allocLocalValue(ty, alignment); const gpa = f.object.dg.gpa; - try f.allocs.put(gpa, decl_c_value.new_local, true); + try f.allocs.put(gpa, decl_c_value.new_local, false); try writer.writeAll("static "); try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete); try writer.writeAll(" = "); @@ -323,14 +319,15 @@ pub const Function = struct { }; } - /// Skips the reuse logic. + /// Skips the reuse logic. This function should be used for any persistent allocation, i.e. + /// those which go into `allocs`. This function does not add the resulting local into `allocs`; + /// that responsibility lies with the caller. fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { const gpa = f.object.dg.gpa; const target = f.object.dg.module.getTarget(); try f.locals.append(gpa, .{ .cty_idx = try f.typeToIndex(ty, .complete), .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), - .is_in_clone = f.is_in_clone, }); return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; } @@ -341,7 +338,8 @@ pub const Function = struct { return result; } - /// Only allocates the local; does not print anything. + /// Only allocates the local; does not print anything. Will attempt to re-use locals, so should + /// not be used for persistent locals (i.e. those in `allocs`). fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { const target = f.object.dg.module.getTarget(); if (f.free_locals_map.getPtr(.{ @@ -2586,7 +2584,7 @@ pub fn genFunc(f: *Function) !void { f.free_locals_map.clearRetainingCapacity(); const main_body = f.air.getMainBody(); - try genBody(f, main_body); + try genBodyResolveState(f, undefined, &.{}, main_body, false); try o.indent_writer.insertNewline(); @@ -2597,8 +2595,8 @@ pub fn genFunc(f: *Function) !void { // alignment, descending. const free_locals = &f.free_locals_map; assert(f.value_map.count() == 0); // there must not be any unfreed locals - for (f.allocs.keys(), f.allocs.values()) |local_index, value| { - if (value) continue; // static + for (f.allocs.keys(), f.allocs.values()) |local_index, should_emit| { + if (!should_emit) continue; const local = f.locals.items[local_index]; log.debug("inserting local {d} into free_locals", .{local_index}); const gop = try free_locals.getOrPut(gpa, local.getType()); @@ -2715,6 +2713,10 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { } } +/// Generate code for an entire body which ends with a `noreturn` instruction. The states of +/// `value_map` and `free_locals_map` are undefined after the generation, and new locals may not +/// have been added to `free_locals_map`. For a version of this function that restores this state, +/// see `genBodyResolveState`. fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { const writer = f.object.writer(); if (body.len == 0) { @@ -2728,10 +2730,69 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO } } +/// Generate code for an entire body which ends with a `noreturn` instruction. The states of +/// `value_map` and `free_locals_map` are restored to their original values, and any non-allocated +/// locals introduced within the body are correctly added to `free_locals_map`. Operands in +/// `leading_deaths` have their deaths processed before the body is generated. +/// A scope is introduced (using braces) only if `inner` is `false`. +/// If `leading_deaths` is empty, `inst` may be `undefined`. +fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) error{ AnalysisFail, OutOfMemory }!void { + if (body.len == 0) { + // Don't go to the expense of cloning everything! + if (!inner) try f.object.writer().writeAll("{}"); + return; + } + + // TODO: we can probably avoid the copies in some other common cases too. + + const gpa = f.object.dg.gpa; + + // Save the original value_map and free_locals_map so that we can restore them after the body. + var old_value_map = try f.value_map.clone(); + defer old_value_map.deinit(); + var old_free_locals = try cloneFreeLocalsMap(gpa, &f.free_locals_map); + defer deinitFreeLocalsMap(gpa, &old_free_locals); + + // Remember how many locals there were before entering the body so that we can free any that + // were newly introduced. Any new locals must necessarily be logically free after the then + // branch is complete. + const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); + + for (leading_deaths) |death| { + try die(f, inst, Air.indexToRef(death)); + } + + if (inner) { + try genBodyInner(f, body); + } else { + try genBody(f, body); + } + + f.value_map.deinit(); + f.value_map = old_value_map.move(); + deinitFreeLocalsMap(gpa, &f.free_locals_map); + f.free_locals_map = old_free_locals.move(); + + // Now, use the lengths we stored earlier to detect any locals the body generated, and free + // them, unless they were used to store allocs. + + for (pre_locals_len..f.locals.items.len) |local_i| { + const local_index = @intCast(LocalIndex, local_i); + if (f.allocs.contains(local_index)) { + continue; + } + try freeLocal(f, inst, local_index, 0); + } +} + fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { const air_tags = f.air.instructions.items(.tag); for (body) |inst| { + if (f.liveness.isUnused(inst) and !f.air.mustLower(inst)) { + continue; + } + const result_value = switch (air_tags[inst]) { // zig fmt: off .constant => unreachable, // excluded from function bodies @@ -3009,11 +3070,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -3032,10 +3088,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const ptr_ty = f.air.typeOf(bin_op.lhs); - if ((!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or - !inst_ty.hasRuntimeBitsIgnoreComptime()) - { + if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } @@ -3074,11 +3127,6 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const ptr_ty = f.air.typeOf(bin_op.lhs); const child_ty = ptr_ty.childType(); @@ -3116,10 +3164,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const slice_ty = f.air.typeOf(bin_op.lhs); - if ((!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or - !inst_ty.hasRuntimeBitsIgnoreComptime()) - { + if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } @@ -3158,11 +3203,6 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const slice_ty = f.air.typeOf(bin_op.lhs); const child_ty = slice_ty.elemType2(); const slice = try f.resolveInst(bin_op.lhs); @@ -3188,7 +3228,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); - if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { + if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } @@ -3224,40 +3264,34 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { } fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; - const inst_ty = f.air.typeOfIndex(inst); const elem_type = inst_ty.elemType(); if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty }; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal( + const local = try f.allocLocalValue( elem_type, - CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), inst_ty.ptrAlignment(target), ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; - try f.allocs.put(gpa, local.new_local, false); + try f.allocs.put(gpa, local.new_local, true); return .{ .local_ref = local.new_local }; } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; - const inst_ty = f.air.typeOfIndex(inst); const elem_ty = inst_ty.elemType(); if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty }; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal( + const local = try f.allocLocalValue( elem_ty, - CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), inst_ty.ptrAlignment(target), ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; - try f.allocs.put(gpa, local.new_local, false); + try f.allocs.put(gpa, local.new_local, true); return .{ .local_ref = local.new_local }; } @@ -3293,9 +3327,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const ptr_info = ptr_scalar_ty.ptrInfo().data; const src_ty = ptr_info.pointee_type; - if (!src_ty.hasRuntimeBitsIgnoreComptime() or - (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) - { + if (!src_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ty_op.operand}); return .none; } @@ -3442,11 +3474,6 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -3470,10 +3497,6 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -3569,10 +3592,6 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const writer = f.object.writer(); @@ -3746,11 +3765,6 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); @@ -3790,11 +3804,6 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const scalar_ty = operand_ty.scalarType(); if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits); - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const op = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -3829,11 +3838,6 @@ fn airBinOp( if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat()) return try airBinBuiltinCall(f, inst, operation, info); - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); @@ -3865,11 +3869,6 @@ fn airCmpOp( data: anytype, operator: std.math.CompareOperator, ) !CValue { - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ data.lhs, data.rhs }); - return .none; - } - const operand_ty = f.air.typeOf(data.lhs); const scalar_ty = operand_ty.scalarType(); @@ -3918,11 +3917,6 @@ fn airEquality( ) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); const operand_bits = operand_ty.bitSize(target); @@ -3987,11 +3981,6 @@ fn airEquality( fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -4008,10 +3997,6 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -4059,11 +4044,6 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(); @@ -4107,11 +4087,6 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const ptr = try f.resolveInst(bin_op.lhs); const len = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); @@ -4316,6 +4291,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Block, ty_pl.payload); const body = f.air.extra[extra.end..][0..extra.data.body_len]; + const liveness_block = f.liveness.getBlock(inst); const block_id: usize = f.next_block_index; f.next_block_index += 1; @@ -4332,7 +4308,15 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { .result = result, }); - try genBodyInner(f, body); + try genBodyResolveState(f, inst, &.{}, body, true); + + assert(f.blocks.remove(inst)); + + // The body might result in some values we had beforehand being killed + for (liveness_block.deaths) |death| { + try die(f, inst, Air.indexToRef(death)); + } + try f.object.indent_writer.insertNewline(); // label might be unused, add a dummy goto // label must be followed by an expression, add an empty one. @@ -4366,6 +4350,7 @@ fn lowerTry( ) !CValue { const err_union = try f.resolveInst(operand); const result_ty = f.air.typeOfIndex(inst); + const liveness_condbr = f.liveness.getCondBr(inst); const writer = f.object.writer(); const payload_ty = err_union_ty.errorUnionPayload(); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); @@ -4389,10 +4374,15 @@ fn lowerTry( } try writer.writeByte(')'); - try genBody(f, body); + try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); try f.object.indent_writer.insertNewline(); } + // Now we have the "then branch" (in terms of the liveness data); process any deaths. + for (liveness_condbr.then_deaths) |death| { + try die(f, inst, Air.indexToRef(death)); + } + if (!payload_has_bits) { if (!operand_is_ptr) { return .none; @@ -4466,10 +4456,6 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; const dest_ty = f.air.typeOfIndex(inst); - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -4593,7 +4579,6 @@ fn airBreakpoint(writer: anytype) !CValue { } fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); @@ -4604,7 +4589,6 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); @@ -4640,7 +4624,7 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("for (;;) "); - try genBody(f, body); + try genBody(f, body); // no need to restore state, we're noreturn try writer.writeByte('\n'); return .none; @@ -4656,61 +4640,24 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { const liveness_condbr = f.liveness.getCondBr(inst); const writer = f.object.writer(); - // Keep using the original for the then branch; use a clone of the value - // map for the else branch. - const gpa = f.object.dg.gpa; - var cloned_map = try f.value_map.clone(); - defer cloned_map.deinit(); - var cloned_frees = try cloneFreeLocalsMap(gpa, &f.free_locals_map); - defer deinitFreeLocalsMap(gpa, &cloned_frees); - - // Remember how many locals there were before entering the then branch so - // that we can notice and use them in the else branch. Any new locals must - // necessarily be free already after the then branch is complete. - const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); - // Remember how many allocs there were before entering the then branch so - // that we can notice and make sure not to use them in the else branch. - // Any new allocs must be removed from the free list. - const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); - const was_in_clone = f.is_in_clone; - f.is_in_clone = true; - - for (liveness_condbr.then_deaths) |operand| { - try die(f, inst, Air.indexToRef(operand)); - } - try writer.writeAll("if ("); try f.writeCValue(writer, cond, .Other); try writer.writeAll(") "); - try genBody(f, then_body); - // TODO: If body ends in goto, elide the else block? - const needs_else = then_body.len <= 0 or f.air.instructions.items(.tag)[then_body[then_body.len - 1]] != .br; - if (needs_else) { - try writer.writeAll(" else "); - } else { - try writer.writeByte('\n'); + try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false); + + // We don't need to use `genBodyResolveState` for the else block, because this instruction is + // noreturn so must terminate a body, therefore we don't need to leave `value_map` or + // `free_locals_map` well defined (our parent is responsible for doing that). + + for (liveness_condbr.else_deaths) |death| { + try die(f, inst, Air.indexToRef(death)); } - f.value_map.deinit(); - f.value_map = cloned_map.move(); - const free_locals = &f.free_locals_map; - deinitFreeLocalsMap(gpa, free_locals); - free_locals.* = cloned_frees.move(); - f.is_in_clone = was_in_clone; - for (liveness_condbr.else_deaths) |operand| { - try die(f, inst, Air.indexToRef(operand)); - } + // We never actually need an else block, because our branches are noreturn so must (for + // instance) `br` to a block (label). - try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); - - if (needs_else) { - try genBody(f, else_body); - } else { - try genBodyInner(f, else_body); - } - - try f.object.indent_writer.insertNewline(); + try genBodyInner(f, else_body); return .none; } @@ -4741,9 +4688,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1); defer gpa.free(liveness.deaths); - // On the final iteration we do not clone the map. This ensures that - // lowering proceeds after the switch_br taking into account the - // mutations to the liveness information. + // On the final iteration we do not need to fix any state. This is because, like in the `else` + // branch of a `cond_br`, our parent has to do it for this entire body anyway. const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); var extra_index: usize = switch_br.end; @@ -4767,56 +4713,23 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(' '); if (case_i != last_case_i) { - const old_value_map = f.value_map; - f.value_map = try old_value_map.clone(); - var free_locals = &f.free_locals_map; - const old_free_locals = free_locals.*; - free_locals.* = try cloneFreeLocalsMap(gpa, free_locals); - - // Remember how many locals there were before entering each branch so that - // we can notice and use them in subsequent branches. Any new locals must - // necessarily be free already after the previous branch is complete. - const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); - // Remember how many allocs there were before entering each branch so that - // we can notice and make sure not to use them in subsequent branches. - // Any new allocs must be removed from the free list. - const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); - const was_in_clone = f.is_in_clone; - f.is_in_clone = true; - - { - defer { - f.is_in_clone = was_in_clone; - f.value_map.deinit(); - deinitFreeLocalsMap(gpa, free_locals); - f.value_map = old_value_map; - free_locals.* = old_free_locals; - } - - for (liveness.deaths[case_i]) |operand| { - try die(f, inst, Air.indexToRef(operand)); - } - - try genBody(f, case_body); - } - - try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); + try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false); } else { - for (liveness.deaths[case_i]) |operand| { - try die(f, inst, Air.indexToRef(operand)); + for (liveness.deaths[case_i]) |death| { + try die(f, inst, Air.indexToRef(death)); } try genBody(f, case_body); } // The case body must be noreturn so we don't need to insert a break. - } const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len]; try f.object.indent_writer.insertNewline(); if (else_body.len > 0) { - for (liveness.deaths[liveness.deaths.len - 1]) |operand| { - try die(f, inst, Air.indexToRef(operand)); + // Note that this must be the last case (i.e. the `last_case_i` case was not hit above) + for (liveness.deaths[liveness.deaths.len - 1]) |death| { + try die(f, inst, Air.indexToRef(death)); } try writer.writeAll("default: "); try genBody(f, else_body); @@ -4843,6 +4756,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const extra = f.air.extraData(Air.Asm, ty_pl.payload); const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); + const gpa = f.object.dg.gpa; var extra_i: usize = extra.end; const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; @@ -4850,8 +4764,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += inputs.len; const result = result: { - if (!is_volatile and f.liveness.isUnused(inst)) break :result .none; - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { @@ -4887,6 +4799,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(output_ty, alignment); + try f.allocs.put(gpa, local_value.new_local, false); try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); @@ -4919,6 +4832,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (is_reg) try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(input_ty, alignment); + try f.allocs.put(gpa, local_value.new_local, false); try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); if (is_reg) { try writer.writeAll(" __asm(\""); @@ -5101,11 +5015,6 @@ fn airIsNull( ) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const writer = f.object.writer(); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -5151,11 +5060,6 @@ fn airIsNull( fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const opt_ty = f.air.typeOf(ty_op.operand); @@ -5203,11 +5107,6 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -5337,11 +5236,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{extra.struct_operand}); - return .none; - } - const container_ptr_val = try f.resolveInst(extra.struct_operand); try reap(f, inst, &.{extra.struct_operand}); const container_ptr_ty = f.air.typeOf(extra.struct_operand); @@ -5351,11 +5245,6 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const container_ptr_val = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const container_ptr_ty = f.air.typeOf(ty_op.operand); @@ -5366,11 +5255,6 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{extra.field_ptr}); - return .none; - } - const target = f.object.dg.module.getTarget(); const container_ptr_ty = f.air.typeOfIndex(inst); const container_ty = container_ptr_ty.childType(); @@ -5489,11 +5373,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{extra.struct_operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{extra.struct_operand}); @@ -5639,11 +5518,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); const operand_ty = f.air.typeOf(ty_op.operand); @@ -5676,11 +5550,6 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -5718,11 +5587,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const payload = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -5764,10 +5628,6 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); @@ -5831,7 +5691,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; + _ = inst; return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); } @@ -5847,10 +5707,6 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue { fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const inst_ty = f.air.typeOfIndex(inst); const payload_ty = inst_ty.errorUnionPayload(); @@ -5885,11 +5741,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const writer = f.object.writer(); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -5923,11 +5774,6 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); @@ -5961,11 +5807,6 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -6009,11 +5850,6 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const inst_ty = f.air.typeOfIndex(inst); @@ -6037,11 +5873,6 @@ fn airUnBuiltinCall( ) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); @@ -6085,11 +5916,6 @@ fn airBinBuiltinCall( ) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } - const operand_ty = f.air.typeOf(bin_op.lhs); const operand_cty = try f.typeToCType(operand_ty, .complete); const is_big = operand_cty.tag() == .array; @@ -6142,11 +5968,6 @@ fn airCmpBuiltinCall( operation: enum { cmp, operator }, info: BuiltinInfo, ) !CValue { - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ data.lhs, data.rhs }); - return .none; - } - const lhs = try f.resolveInst(data.lhs); const rhs = try f.resolveInst(data.rhs); try reap(f, inst, &.{ data.lhs, data.rhs }); @@ -6317,9 +6138,6 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(atomic_load.ptr); try reap(f, inst, &.{atomic_load.ptr}); const ptr_ty = f.air.typeOf(atomic_load.ptr); - if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { - return .none; - } const inst_ty = f.air.typeOfIndex(inst); const writer = f.object.writer(); @@ -6463,11 +6281,6 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -6491,11 +6304,6 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const inst_ty = f.air.typeOfIndex(inst); const enum_ty = f.air.typeOf(un_op); const operand = try f.resolveInst(un_op); @@ -6516,11 +6324,6 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); @@ -6537,11 +6340,6 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } - const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); @@ -6573,11 +6371,6 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Bin, pl_op.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); - return .none; - } - const pred = try f.resolveInst(pl_op.operand); const lhs = try f.resolveInst(extra.lhs); const rhs = try f.resolveInst(extra.rhs); @@ -6609,11 +6402,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ extra.a, extra.b }); - return .none; - } - const mask = f.air.values[extra.mask]; const lhs = try f.resolveInst(extra.a); const rhs = try f.resolveInst(extra.b); @@ -6655,11 +6443,6 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const reduce = f.air.instructions.items(.data)[inst].reduce; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{reduce.operand}); - return .none; - } - const target = f.object.dg.module.getTarget(); const scalar_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(reduce.operand); @@ -6831,8 +6614,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } } - if (f.liveness.isUnused(inst)) return .none; - const target = f.object.dg.module.getTarget(); const writer = f.object.writer(); @@ -6999,11 +6780,6 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{extra.init}); - return .none; - } - const union_ty = f.air.typeOfIndex(inst); const target = f.object.dg.module.getTarget(); const union_obj = union_ty.cast(Type.Payload.Union).?.data; @@ -7070,8 +6846,6 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { } fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; - const pl_op = f.air.instructions.items(.data)[inst].pl_op; const writer = f.object.writer(); @@ -7104,10 +6878,6 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -7133,10 +6903,6 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{un_op}); - return .none; - } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -7164,10 +6930,6 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -7200,10 +6962,6 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); - return .none; - } const mulend1 = try f.resolveInst(bin_op.lhs); const mulend2 = try f.resolveInst(bin_op.rhs); @@ -7236,8 +6994,6 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { } fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return .none; - const inst_ty = f.air.typeOfIndex(inst); const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; @@ -7256,10 +7012,6 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const inst_ty = f.air.typeOfIndex(inst); const va_list = try f.resolveInst(ty_op.operand); @@ -7291,10 +7043,6 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - if (f.liveness.isUnused(inst)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } const inst_ty = f.air.typeOfIndex(inst); const va_list = try f.resolveInst(ty_op.operand); @@ -7858,7 +7606,6 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in const gpa = f.object.dg.gpa; const local = &f.locals.items[local_index]; log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst }); - if (f.is_in_clone != local.is_in_clone) return; const gop = try f.free_locals_map.getOrPut(gpa, local.getType()); if (!gop.found_existing) gop.value_ptr.* = .{}; if (std.debug.runtime_safety) { @@ -7916,35 +7663,3 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { } map.deinit(gpa); } - -fn noticeBranchFrees( - f: *Function, - pre_locals_len: LocalIndex, - pre_allocs_len: LocalIndex, - inst: Air.Inst.Index, -) !void { - for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| { - const local_index = @intCast(LocalIndex, local_i); - if (f.allocs.contains(local_index)) { - if (std.debug.runtime_safety) { - // new allocs are no longer freeable, so make sure they aren't in the free list - if (f.free_locals_map.getPtr(local.getType())) |locals_list| { - assert(!locals_list.contains(local_index)); - } - } - continue; - } - - // free cloned locals from other branches at current cloned-ness - std.debug.assert(local.is_in_clone or !f.is_in_clone); - local.is_in_clone = f.is_in_clone; - try freeLocal(f, inst, local_index, 0); - } - - for (f.allocs.keys()[pre_allocs_len..]) |local_i| { - const local_index = @intCast(LocalIndex, local_i); - const local = &f.locals.items[local_index]; - // new allocs are no longer freeable, so remove them from the free list - if (f.free_locals_map.getPtr(local.getType())) |locals_list| _ = locals_list.swapRemove(local_index); - } -}