diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-12-04 21:59:01 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-04 21:59:01 -0500 |
| commit | 70ad5bc3631c330eb41b9379d7e93f0647da2fd6 (patch) | |
| tree | 8b9e7c78245891efee3cc8b4f0e3ad770704bb7e /src | |
| parent | bdb6fb57639a4508dda523301c5eaf31e8d89edf (diff) | |
| parent | 7d3cc3bc8d772519d390b7f13346eeab73bc0c21 (diff) | |
| download | zig-70ad5bc3631c330eb41b9379d7e93f0647da2fd6.tar.gz zig-70ad5bc3631c330eb41b9379d7e93f0647da2fd6.zip | |
Merge pull request #13768 from ziglang/cbe-reuse-locals-2
C backend: reuse locals but respect loops to pass behavior tests
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 14 | ||||
| -rw-r--r-- | src/codegen/c.zig | 2418 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 10 | ||||
| -rw-r--r-- | src/link/C.zig | 12 |
4 files changed, 1622 insertions, 832 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1d24076154..d4e3559006 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2001,7 +2001,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { try func.restoreStackPointer(); try func.addTag(.@"return"); - return func.finishAir(inst, .none, &.{}); + return func.finishAir(inst, .none, &.{un_op}); } fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!void { @@ -3161,7 +3161,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } break :result func.reuseOperand(ty_op.operand, operand); } else WValue{ .none = {} }; - func.finishAir(inst, result, &.{}); + func.finishAir(inst, result, &.{ty_op.operand}); } fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { @@ -4115,7 +4115,7 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const len = try func.resolveInst(bin_op.rhs); try func.memset(ptr, len, value); - func.finishAir(inst, .none, &.{pl_op.operand}); + func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs }); } /// Sets a region of memory at `ptr` to the value of `value` @@ -4424,6 +4424,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { else => unreachable, } }; + // TODO: this is incorrect Liveness handling code func.finishAir(inst, result, &.{}); } @@ -4747,7 +4748,7 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const len = try func.resolveInst(bin_op.rhs); try func.memcpy(dst, src, len); - func.finishAir(inst, .none, &.{pl_op.operand}); + func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs }); } fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { @@ -5158,7 +5159,8 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const pl_op = func.air.instructions.items(.data)[inst].pl_op; const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; - if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); + if (func.liveness.isUnused(inst)) + return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); const ty = func.air.typeOfIndex(inst); if (ty.zigTypeTag() == .Vector) { @@ -5186,7 +5188,7 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { break :result try (try func.binOp(mul_result, addend, ty, .add)).toLocal(func, ty); }; - func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); + func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); } fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7fd76a6962..bfd2ff7a3a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -30,10 +30,9 @@ const BigInt = std.math.big.int; pub const CValue = union(enum) { none: void, - /// Index into local_names - local: usize, - /// Index into local_names, but take the address. - local_ref: usize, + local: LocalIndex, + /// Address of a local. + local_ref: LocalIndex, /// A constant instruction, to be rendered inline. constant: Air.Inst.Ref, /// Index into the parameters @@ -50,8 +49,6 @@ pub const CValue = union(enum) { /// Render these bytes literally. /// TODO make this a [*:0]const u8 to save memory bytes: []const u8, - /// Index of an instruction that should later be rendered inline. - inline_index: Air.Inst.Index, }; const BlockData = struct { @@ -72,6 +69,19 @@ pub const TypedefMap = std.ArrayHashMap( true, ); +const LoopDepth = u16; +const Local = struct { + ty: Type, + alignment: u32, + /// How many loops the last definition was nested in. + loop_depth: LoopDepth, +}; + +const LocalIndex = u16; +const LocalsList = std.ArrayListUnmanaged(LocalIndex); +const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true); +const LocalsStack = std.ArrayListUnmanaged(LocalsMap); + const FormatTypeAsCIdentContext = struct { ty: Type, mod: *Module, @@ -81,7 +91,6 @@ const ValueRenderLocation = enum { FunctionArgument, Initializer, Other, - condition, }; const BuiltinInfo = enum { @@ -254,10 +263,29 @@ pub const Function = struct { value_map: CValueMap, blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, next_arg_index: usize = 0, - next_local_index: usize = 0, next_block_index: usize = 0, object: Object, func: *Module.Fn, + /// 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_stack: LocalsStack = .{}, + free_locals_clone_depth: LoopDepth = 0, + /// Locals which will not be freed by Liveness. This is used after a + /// Function body is lowered in order to make `free_locals_stack` 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. + allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{}, + /// Needed for memory used by the keys of free_locals_stack entries. + arena: std.heap.ArenaAllocator, + + fn tyHashCtx(f: Function) Type.HashContext32 { + return .{ .mod = f.object.dg.module }; + } fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue { const gop = try f.value_map.getOrPut(inst); @@ -268,9 +296,12 @@ pub const Function = struct { const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { const writer = f.object.code_header.writer(); - const decl_c_value = f.allocLocalValue(); + 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.local, true); try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, 0, .Complete); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, alignment, .Complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val, .Initializer); try writer.writeAll(";\n "); @@ -281,19 +312,6 @@ pub const Function = struct { return result; } - fn resolveInstNoInline(f: *Function, inst: Air.Inst.Ref) !CValue { - const operand = try f.resolveInst(inst); - if (operand != .inline_index) return operand; - - const inst_ty = f.air.typeOf(inst); - const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - try f.writeCValueInline(operand.inline_index); - try writer.writeAll(";\n"); - return local; - } - fn wantSafety(f: *Function) bool { return switch (f.object.dg.module.optimizeMode()) { .Debug, .ReleaseSafe => true, @@ -301,27 +319,43 @@ pub const Function = struct { }; } - fn allocLocalValue(f: *Function) CValue { - const result = f.next_local_index; - f.next_local_index += 1; - return .{ .local = result }; + fn getFreeLocals(f: *Function) *LocalsMap { + return &f.free_locals_stack.items[f.free_locals_stack.items.len - 1]; + } + + /// Skips the reuse logic. + fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { + const gpa = f.object.dg.gpa; + try f.locals.append(gpa, .{ + .ty = ty, + .alignment = alignment, + .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), + }); + return CValue{ .local = @intCast(LocalIndex, f.locals.items.len - 1) }; } - fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue { - return f.allocAlignedLocal(ty, mutability, 0); + fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { + const result = try f.allocAlignedLocal(ty, .Mut, 0); + log.debug("%{d}: allocating t{d}", .{ inst, result.local }); + return result; } + /// Only allocates the local; does not print anything. fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue { - const local_value = f.allocLocalValue(); - try f.object.dg.renderTypeAndName( - f.object.writer(), - ty, - local_value, - mutability, - alignment, - .Complete, - ); - return local_value; + _ = mutability; + + if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| { + for (locals_list.items) |local_index, i| { + const local = &f.locals.items[local_index]; + if (local.alignment >= alignment) { + local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); + _ = locals_list.swapRemove(i); + return CValue{ .local = local_index }; + } + } + } + + return try f.allocLocalValue(ty, alignment); } fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void { @@ -329,95 +363,10 @@ pub const Function = struct { .constant => |inst| { const ty = f.air.typeOf(inst); const val = f.air.value(inst).?; - try f.object.dg.renderValue(w, ty, val, location); + return f.object.dg.renderValue(w, ty, val, location); }, - .undef => |ty| try f.object.dg.renderValue(w, ty, Value.undef, location), - .inline_index => |node| { - if (location != .condition) try w.writeByte('('); - try f.writeCValueInline(node); - if (location != .condition) try w.writeByte(')'); - }, - else => try f.object.dg.writeCValue(w, c_value), - } - } - - const E = error{ OutOfMemory, AnalysisFail }; - - fn writeCValueInline(f: *Function, inst: Air.Inst.Index) E!void { - switch (f.air.instructions.items(.tag)[inst]) { - // zig fmt: off - // TODO use a different strategy for add, sub, mul, div - // that communicates to the optimizer that wrapping is UB. - .add => try airBinOp(f, inst, "+", "add", .None), - .sub => try airBinOp(f, inst, "-", "sub", .None), - .mul => try airBinOp(f, inst, "*", "mul", .None), - - .div_float => try airBinBuiltinCall(f, inst, "div", .None), - - .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None), - .rem => { - const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const lhs_ty = f.air.typeOf(bin_op.lhs); - // For binary operations @TypeOf(lhs)==@TypeOf(rhs), - // so we only check one. - if (lhs_ty.isInt()) - try airBinOp(f, inst, "%", "rem", .None) - else - try airBinFloatOp(f, inst, "fmod"); - }, - .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None), - .mod => try airBinBuiltinCall(f, inst, "mod", .None), - - .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits), - .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits), - .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits), - - .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits), - .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits), - .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits), - .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits), - - .min => try airMinMax(f, inst, '<', "fmin"), - .max => try airMinMax(f, inst, '>', "fmax"), - - .cmp_gt => try airCmpOp(f, inst, ">", "gt"), - .cmp_gte => try airCmpOp(f, inst, ">=", "ge"), - .cmp_lt => try airCmpOp(f, inst, "<", "lt"), - .cmp_lte => try airCmpOp(f, inst, "<=", "le"), - - .cmp_eq => try airEquality(f, inst, "((", "==", "eq"), - .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"), - - .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), - .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None), - .xor => try airBinOp(f, inst, "^", "xor", .None), - .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None), - .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits), - .shl_exact => try airBinOp(f, inst, "<<", "shl", .None), - .not => try airNot (f, inst), - - .is_err => try airIsErr(f, inst, false, "!="), - .is_non_err => try airIsErr(f, inst, false, "=="), - .is_err_ptr => try airIsErr(f, inst, true, "!="), - .is_non_err_ptr => try airIsErr(f, inst, true, "=="), - - .is_null => try airIsNull(f, inst, "==", false), - .is_non_null => try airIsNull(f, inst, "!=", false), - .is_null_ptr => try airIsNull(f, inst, "==", true), - .is_non_null_ptr => try airIsNull(f, inst, "!=", true), - - .get_union_tag => try airGetUnionTag(f, inst), - .clz => try airUnBuiltinCall(f, inst, "clz", .Bits), - .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits), - .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits), - .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits), - .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits), - .tag_name => try airTagName(f, inst), - .error_name => try airErrorName(f, inst), - - .ptrtoint => try airPtrToInt(f, inst), - else => unreachable, - // zig fmt: on + .undef => |ty| return f.object.dg.renderValue(w, ty, Value.undef, location), + else => return f.object.dg.writeCValue(w, c_value), } } @@ -443,7 +392,6 @@ pub const Function = struct { try w.writeByte('.'); return f.writeCValue(w, member, .Other); }, - .inline_index => unreachable, // Use resolveInstNoInline else => return f.object.dg.writeCValueMember(w, c_value, member), } } @@ -477,6 +425,24 @@ pub const Function = struct { fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { return f.object.dg.fmtIntLiteral(ty, val); } + + pub fn deinit(f: *Function, gpa: mem.Allocator) void { + f.allocs.deinit(gpa); + f.locals.deinit(gpa); + for (f.free_locals_stack.items) |*free_locals| { + deinitFreeLocalsMap(gpa, free_locals); + } + f.free_locals_stack.deinit(gpa); + f.blocks.deinit(gpa); + f.value_map.deinit(); + f.object.code.deinit(); + for (f.object.dg.typedefs.values()) |typedef| { + gpa.free(typedef.rendered); + } + f.object.dg.typedefs.deinit(); + f.object.dg.fwd_decl.deinit(); + f.arena.deinit(); + } }; /// This data is available when outputting .c code for a `Module`. @@ -2267,7 +2233,7 @@ pub const DeclGen = struct { fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { - .none, .inline_index => unreachable, + .none => unreachable, .local => |i| return w.print("t{d}", .{i}), .local_ref => |i| return w.print("&t{d}", .{i}), .constant => unreachable, @@ -2286,7 +2252,7 @@ pub const DeclGen = struct { fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { - .none, .inline_index => unreachable, + .none => unreachable, .local => |i| return w.print("(*t{d})", .{i}), .local_ref => |i| return w.print("t{d}", .{i}), .constant => unreachable, @@ -2316,7 +2282,7 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { - .none, .constant, .field, .undef, .inline_index => unreachable, + .none, .constant, .field, .undef => unreachable, .local, .arg, .decl, .identifier, .bytes => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); @@ -2502,12 +2468,13 @@ pub fn genFunc(f: *Function) !void { defer tracy.end(); const o = &f.object; + const gpa = o.dg.gpa; const tv: TypedValue = .{ .ty = o.dg.decl.ty, .val = o.dg.decl.val, }; - o.code_header = std.ArrayList(u8).init(f.object.dg.gpa); + o.code_header = std.ArrayList(u8).init(gpa); defer o.code_header.deinit(); const is_global = o.dg.declIsGlobal(tv); @@ -2529,11 +2496,59 @@ pub fn genFunc(f: *Function) !void { o.code_header.appendSliceAssumeCapacity("{\n "); const empty_header_len = o.code_header.items.len; + f.free_locals_stack.clearRetainingCapacity(); + try f.free_locals_stack.append(gpa, .{}); + const main_body = f.air.getMainBody(); try genBody(f, main_body); try o.indent_writer.insertNewline(); + // Take advantage of the free_locals map to bucket locals per type. All + // locals corresponding to AIR instructions should be in there due to + // Liveness analysis, however, locals from alloc instructions will be + // missing. These are added now to complete the map. Then we can sort by + // alignment, descending. + const free_locals = f.getFreeLocals(); + const values = f.allocs.values(); + for (f.allocs.keys()) |local_index, i| { + if (values[i]) continue; // static + const local = f.locals.items[local_index]; + log.debug("inserting local {d} into free_locals", .{local_index}); + const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx()); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, local_index); + } + + const SortContext = struct { + target: std.Target, + keys: []const Type, + + pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { + const a_ty = ctx.keys[a_index]; + const b_ty = ctx.keys[b_index]; + return b_ty.abiAlignment(ctx.target) < a_ty.abiAlignment(ctx.target); + } + }; + const target = o.dg.module.getTarget(); + free_locals.sort(SortContext{ .target = target, .keys = free_locals.keys() }); + + const w = o.code_header.writer(); + for (free_locals.values()) |list| { + for (list.items) |local_index| { + const local = f.locals.items[local_index]; + try o.dg.renderTypeAndName( + w, + local.ty, + .{ .local = local_index }, + .Mut, + local.alignment, + .Complete, + ); + try w.writeAll(";\n "); + } + } + // If we have a header to insert, append the body to the header // and then return the result, freeing the body. if (o.code_header.items.len > empty_header_len) { @@ -2655,41 +2670,51 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .ptr_add => try airPtrAddSub(f, inst, '+'), .ptr_sub => try airPtrAddSub(f, inst, '-'), - .add => CValue{ .inline_index = inst }, - .sub => CValue{ .inline_index = inst }, - .mul => CValue{ .inline_index = inst }, + // TODO use a different strategy for add, sub, mul, div + // that communicates to the optimizer that wrapping is UB. + .add => try airBinOp(f, inst, "+", "add", .None), + .sub => try airBinOp(f, inst, "-", "sub", .None), + .mul => try airBinOp(f, inst, "*", "mul", .None), .neg => try airFloatNeg(f, inst), - .div_float => CValue{ .inline_index = inst }, - - .div_trunc, .div_exact => CValue{ .inline_index = inst }, - .rem => CValue{ .inline_index = inst }, - .div_floor => CValue{ .inline_index = inst }, - .mod => CValue{ .inline_index = inst }, - - .addwrap => CValue{ .inline_index = inst }, - .subwrap => CValue{ .inline_index = inst }, - .mulwrap => CValue{ .inline_index = inst }, - - .add_sat => CValue{ .inline_index = inst }, - .sub_sat => CValue{ .inline_index = inst }, - .mul_sat => CValue{ .inline_index = inst }, - .shl_sat => CValue{ .inline_index = inst }, - - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .fabs, - .floor, - .ceil, - .round, - => |tag| try airUnFloatOp(f, inst, @tagName(tag)), + .div_float => try airBinBuiltinCall(f, inst, "div", .None), + + .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None), + .rem => blk: { + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const lhs_ty = f.air.typeOf(bin_op.lhs); + // For binary operations @TypeOf(lhs)==@TypeOf(rhs), + // so we only check one. + break :blk if (lhs_ty.isInt()) + try airBinOp(f, inst, "%", "rem", .None) + else + try airBinFloatOp(f, inst, "fmod"); + }, + .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None), + .mod => try airBinBuiltinCall(f, inst, "mod", .None), + + .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits), + .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits), + .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits), + + .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits), + .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits), + .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits), + .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits), + + .sqrt => try airUnFloatOp(f, inst, "sqrt"), + .sin => try airUnFloatOp(f, inst, "sin"), + .cos => try airUnFloatOp(f, inst, "cos"), + .tan => try airUnFloatOp(f, inst, "tan"), + .exp => try airUnFloatOp(f, inst, "exp"), + .exp2 => try airUnFloatOp(f, inst, "exp2"), + .log => try airUnFloatOp(f, inst, "log"), + .log2 => try airUnFloatOp(f, inst, "log2"), + .log10 => try airUnFloatOp(f, inst, "log10"), + .fabs => try airUnFloatOp(f, inst, "fabs"), + .floor => try airUnFloatOp(f, inst, "floor"), + .ceil => try airUnFloatOp(f, inst, "ceil"), + .round => try airUnFloatOp(f, inst, "round"), .trunc_float => try airUnFloatOp(f, inst, "trunc"), .mul_add => try airMulAdd(f, inst), @@ -2699,45 +2724,45 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits), .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits), - .min => CValue{ .inline_index = inst }, - .max => CValue{ .inline_index = inst }, + .min => try airMinMax(f, inst, '<', "fmin"), + .max => try airMinMax(f, inst, '>', "fmax"), .slice => try airSlice(f, inst), - .cmp_gt => CValue{ .inline_index = inst }, - .cmp_gte => CValue{ .inline_index = inst }, - .cmp_lt => CValue{ .inline_index = inst }, - .cmp_lte => CValue{ .inline_index = inst }, + .cmp_gt => try airCmpOp(f, inst, ">", "gt"), + .cmp_gte => try airCmpOp(f, inst, ">=", "ge"), + .cmp_lt => try airCmpOp(f, inst, "<", "lt"), + .cmp_lte => try airCmpOp(f, inst, "<=", "le"), - .cmp_eq => CValue{ .inline_index = inst }, - .cmp_neq => CValue{ .inline_index = inst }, + .cmp_eq => try airEquality(f, inst, "((", "==", "eq"), + .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"), .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}), .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), // bool_and and bool_or are non-short-circuit operations - .bool_and, .bit_and => CValue{ .inline_index = inst }, - .bool_or, .bit_or => CValue{ .inline_index = inst }, - .xor => CValue{ .inline_index = inst }, - .shr, .shr_exact => CValue{ .inline_index = inst }, - .shl, => CValue{ .inline_index = inst }, - .shl_exact => CValue{ .inline_index = inst }, - .not => CValue{ .inline_index = inst }, + .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), + .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None), + .xor => try airBinOp(f, inst, "^", "xor", .None), + .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None), + .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits), + .shl_exact => try airBinOp(f, inst, "<<", "shl", .None), + .not => try airNot (f, inst), .optional_payload => try airOptionalPayload(f, inst), .optional_payload_ptr => try airOptionalPayloadPtr(f, inst), .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst), .wrap_optional => try airWrapOptional(f, inst), - .is_err => CValue{ .inline_index = inst }, - .is_non_err => CValue{ .inline_index = inst }, - .is_err_ptr => CValue{ .inline_index = inst }, - .is_non_err_ptr => CValue{ .inline_index = inst }, + .is_err => try airIsErr(f, inst, false, "!="), + .is_non_err => try airIsErr(f, inst, false, "=="), + .is_err_ptr => try airIsErr(f, inst, true, "!="), + .is_non_err_ptr => try airIsErr(f, inst, true, "=="), - .is_null => CValue{ .inline_index = inst }, - .is_non_null => CValue{ .inline_index = inst }, - .is_null_ptr => CValue{ .inline_index = inst }, - .is_non_null_ptr => CValue{ .inline_index = inst }, + .is_null => try airIsNull(f, inst, "==", false), + .is_non_null => try airIsNull(f, inst, "!=", false), + .is_null_ptr => try airIsNull(f, inst, "==", true), + .is_non_null_ptr => try airIsNull(f, inst, "!=", true), .alloc => try airAlloc(f, inst), .ret_ptr => try airRetPtr(f, inst), @@ -2765,14 +2790,14 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .memset => try airMemset(f, inst), .memcpy => try airMemcpy(f, inst), .set_union_tag => try airSetUnionTag(f, inst), - .get_union_tag => CValue{ .inline_index = inst }, - .clz => CValue{ .inline_index = inst }, - .ctz => CValue{ .inline_index = inst }, - .popcount => CValue{ .inline_index = inst }, - .byte_swap => CValue{ .inline_index = inst }, - .bit_reverse => CValue{ .inline_index = inst }, - .tag_name => CValue{ .inline_index = inst }, - .error_name => CValue{ .inline_index = inst }, + .get_union_tag => try airGetUnionTag(f, inst), + .clz => try airUnBuiltinCall(f, inst, "clz", .Bits), + .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits), + .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits), + .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits), + .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits), + .tag_name => try airTagName(f, inst), + .error_name => try airErrorName(f, inst), .splat => try airSplat(f, inst), .select => try airSelect(f, inst), .shuffle => try airShuffle(f, inst), @@ -2808,7 +2833,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .fpext, => try airFloatCast(f, inst), - .ptrtoint => CValue{ .inline_index = inst }, + .ptrtoint => try airPtrToInt(f, inst), .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)), .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)), @@ -2877,6 +2902,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), // zig fmt: on }; + if (result_value == .local) { + log.debug("map %{d} to t{d}", .{ inst, result_value.local }); + } switch (result_value) { .none => {}, else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value), @@ -2885,13 +2913,19 @@ 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 { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const operand = try f.resolveInstNoInline(ty_op.operand); + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); if (is_ptr) { try writer.writeByte('&'); @@ -2906,22 +2940,29 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { 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()) return CValue.none; + !inst_ty.hasRuntimeBitsIgnoreComptime()) + { + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return CValue.none; + } const ptr = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(inst_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else try writer.writeAll(" = "); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } try f.writeCValue(writer, ptr, .Other); try writer.writeByte('['); try f.writeCValue(writer, index, .Other); @@ -2936,19 +2977,28 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { } fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.none; + } + const ptr_ty = f.air.typeOf(bin_op.lhs); const child_ty = ptr_ty.childType(); const ptr = try f.resolveInst(bin_op.lhs); - if (!child_ty.hasRuntimeBitsIgnoreComptime()) return ptr; + if (!child_ty.hasRuntimeBitsIgnoreComptime()) { + if (f.liveness.operandDies(inst, 1)) try die(f, inst, bin_op.rhs); + return ptr; + } const index = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const writer = f.object.writer(); - const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); + const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = &("); if (ptr_ty.ptrSize() == .One) { // It's a pointer to an array, so we need to de-reference. @@ -2967,22 +3017,29 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { 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()) return CValue.none; + !inst_ty.hasRuntimeBitsIgnoreComptime()) + { + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return CValue.none; + } const slice = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(inst_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else try writer.writeAll(" = "); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } try f.writeCValue(writer, slice, .Other); try writer.writeAll(".ptr["); try f.writeCValue(writer, index, .Other); @@ -2997,23 +3054,28 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { } fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.none; + } + const slice_ty = f.air.typeOf(bin_op.lhs); const child_ty = slice_ty.elemType2(); const slice = try f.resolveInst(bin_op.lhs); + const index = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const writer = f.object.writer(); - const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); + const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); if (child_ty.hasRuntimeBitsIgnoreComptime()) try writer.writeByte('&'); try f.writeCValue(writer, slice, .Other); try writer.writeAll(".ptr"); if (child_ty.hasRuntimeBitsIgnoreComptime()) { - const index = try f.resolveInst(bin_op.rhs); try writer.writeByte('['); try f.writeCValue(writer, index, .Other); try writer.writeByte(']'); @@ -3023,24 +3085,30 @@ 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()) return CValue.none; + if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return CValue.none; + } - const bin_op = f.air.instructions.items(.data)[inst].bin_op; const array = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(inst_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else try writer.writeAll(" = "); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } try f.writeCValue(writer, array, .Other); try writer.writeByte('['); try f.writeCValue(writer, index, .Other); @@ -3055,36 +3123,36 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { } fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const elem_type = inst_ty.elemType(); - const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { return CValue{ .undef = inst_ty }; } + const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; const target = f.object.dg.module.getTarget(); - // First line: the variable used as data storage. const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); - try writer.writeAll(";\n"); - + log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); + const gpa = f.object.dg.module.gpa; + try f.allocs.put(gpa, local.local, false); return CValue{ .local_ref = local.local }; } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const elem_ty = inst_ty.elemType(); - if (!elem_ty.hasRuntimeBitsIgnoreComptime()) { + if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { return CValue{ .undef = inst_ty }; } - // First line: the variable used as data storage. - const local = try f.allocLocal(elem_ty, .Mut); - try writer.writeAll(";\n"); - + const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; + const target = f.object.dg.module.getTarget(); + const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); + log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); + const gpa = f.object.dg.module.gpa; + try f.allocs.put(gpa, local.local, false); return CValue{ .local_ref = local.local }; } @@ -3100,21 +3168,25 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const src_ty = ptr_info.pointee_type; if (!src_ty.hasRuntimeBitsIgnoreComptime() or - !ptr_info.@"volatile" and f.liveness.isUnused(inst)) + (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) + { + try reap(f, inst, &.{ty_op.operand}); return CValue.none; + } + + const operand = try f.resolveInst(ty_op.operand); + + try reap(f, inst, &.{ty_op.operand}); const target = f.object.dg.module.getTarget(); const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); const is_array = lowersToArray(src_ty, target); const need_memcpy = !is_aligned or is_array; - const operand = try f.resolveInst(ty_op.operand); const writer = f.object.writer(); - // We need to initialize arrays and unaligned loads with a memcpy so they must be mutable. - const local = try f.allocLocal(src_ty, if (need_memcpy) .Mut else .Const); + const local = try f.allocLocal(inst, src_ty); if (need_memcpy) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); if (!is_array) try writer.writeByte('&'); try f.writeCValue(writer, local, .FunctionArgument); @@ -3148,6 +3220,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { }; const field_ty = Type.initPayload(&field_pl.base); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, src_ty); try writer.writeAll(")zig_wrap_"); @@ -3162,6 +3235,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits); try writer.writeByte(')'); } else { + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValueDeref(writer, operand); } @@ -3181,9 +3255,10 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { var deref = is_ptr; const operand = try f.resolveInst(un_op); - const ret_val = if (lowersToArray(ret_ty, target)) ret_val: { - const array_local = try f.allocLocal(lowered_ret_ty, .Mut); - try writer.writeAll(";\n"); + try reap(f, inst, &.{un_op}); + const is_array = lowersToArray(ret_ty, target); + const ret_val = if (is_array) ret_val: { + const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); try writer.writeAll("memcpy("); try f.writeCValueMember(writer, array_local, .{ .field = 0 }); try writer.writeAll(", "); @@ -3204,23 +3279,33 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { else try f.writeCValue(writer, ret_val, .Other); try writer.writeAll(";\n"); - } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { - // Not even allowed to return void in a naked function. - try writer.writeAll("return;\n"); + if (is_array) { + try freeLocal(f, inst, ret_val.local, 0); + } + } else { + try reap(f, inst, &.{un_op}); + if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { + // Not even allowed to return void in a naked function. + try writer.writeAll("return;\n"); + } } return CValue.none; } fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); return CValue.none; + } - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const operand = try f.resolveInst(ty_op.operand); - + try reap(f, inst, &.{ty_op.operand}); const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); @@ -3230,17 +3315,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { } fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const writer = f.object.writer(); - const operand = try f.resolveInst(ty_op.operand); + const local = try f.allocLocal(inst, inst_ty); const target = f.object.dg.module.getTarget(); const dest_int_info = inst_ty.intInfo(target); const dest_bits = dest_int_info.bits; + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); @@ -3282,20 +3372,24 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { } fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; const un_op = f.air.instructions.items(.data)[inst].un_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const operand = try f.resolveInst(un_op); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } -fn airStoreUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { +fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { if (f.wantSafety()) { const writer = f.object.writer(); try writer.writeAll("memset("); @@ -3311,18 +3405,23 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // *a = b; const bin_op = f.air.instructions.items(.data)[inst].bin_op; const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; - if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) return CValue.none; + if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return CValue.none; + } const ptr_val = try f.resolveInst(bin_op.lhs); const src_ty = f.air.typeOf(bin_op.rhs); const src_val = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + // TODO Sema should emit a different instruction when the store should // possibly do the safety 0xaa bytes for undefined. const src_val_is_undefined = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; if (src_val_is_undefined) - return try airStoreUndefined(f, ptr_info.pointee_type, ptr_val); + return try storeUndefined(f, ptr_info.pointee_type, ptr_val); const target = f.object.dg.module.getTarget(); const is_aligned = ptr_info.@"align" == 0 or @@ -3340,7 +3439,8 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // so work around this by initializing into new local. // TODO this should be done by manually initializing elements of the dest array const array_src = if (src_val == .constant) blk: { - const new_local = try f.allocLocal(src_ty, .Const); + const new_local = try f.allocLocal(inst, src_ty); + try f.writeCValue(writer, new_local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, src_val, .Initializer); try writer.writeAll(";\n"); @@ -3356,6 +3456,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", sizeof("); try f.renderTypecast(writer, src_ty); try writer.writeAll("))"); + if (src_val == .constant) { + try freeLocal(f, inst, array_src.local, 0); + } } else if (ptr_info.host_size != 0) { const host_bits = ptr_info.host_size * 8; var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; @@ -3421,22 +3524,24 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { } fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; - 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 CValue.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 }); const inst_ty = f.air.typeOfIndex(inst); const vector_ty = f.air.typeOf(bin_op.lhs); const scalar_ty = vector_ty.scalarType(); const w = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Mut); - try w.writeAll(";\n"); + const local = try f.allocLocal(inst, inst_ty); switch (vector_ty.zigTypeTag()) { .Vector => { @@ -3471,21 +3576,27 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: return local; } -fn airNot(f: *Function, inst: Air.Inst.Index) !void { +fn airNot(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 CValue.none; + } + const op = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - - const target = f.object.dg.module.getTarget(); - if (inst_ty.bitSize(target) > 64) {} - - try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); - try writer.writeByte(')'); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); try writer.writeByte(if (inst_ty.tag() == .bool) '!' else '~'); try f.writeCValue(writer, op, .Other); + try writer.writeAll(";\n"); + + return local; } fn airBinOp( @@ -3494,54 +3605,68 @@ fn airBinOp( operator: []const u8, operation: []const u8, info: BuiltinInfo, -) !void { +) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); if ((operand_ty.isInt() and operand_ty.bitSize(target) > 64) or operand_ty.isRuntimeFloat()) - return airBinBuiltinCall(f, inst, operation, info); + return try airBinBuiltinCall(f, inst, operation, info); - const inst_ty = f.air.typeOfIndex(inst); 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 }); + + if (f.liveness.isUnused(inst)) return CValue.none; + + const inst_ty = f.air.typeOfIndex(inst); + const writer = f.object.writer(); - try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); - try writer.writeAll(")("); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); try f.writeCValue(writer, lhs, .Other); try writer.writeByte(' '); try writer.writeAll(operator); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); - try writer.writeByte(')'); + try writer.writeAll(";\n"); + + return local; } -fn airCmpOp( - f: *Function, - inst: Air.Inst.Index, - operator: []const u8, - operation: []const u8, -) !void { +fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const 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 CValue.none; + } + const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) - return airCmpBuiltinCall(f, inst, operator, "cmp"); + return try cmpBuiltinCall(f, inst, operator, "cmp"); if (operand_ty.isRuntimeFloat()) - return airCmpBuiltinCall(f, inst, operator, operation); + return try cmpBuiltinCall(f, inst, operator, operation); + const inst_ty = f.air.typeOfIndex(inst); 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 }); const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); try f.writeCValue(writer, lhs, .Other); try writer.writeByte(' '); try writer.writeAll(operator); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); + try writer.writeAll(";\n"); + + return local; } fn airEquality( @@ -3550,20 +3675,31 @@ fn airEquality( negate_prefix: []const u8, operator: []const u8, operation: []const u8, -) !void { +) !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 CValue.none; + } + const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) - return airCmpBuiltinCall(f, inst, operator, "cmp"); + return try cmpBuiltinCall(f, inst, operator, "cmp"); if (operand_ty.isRuntimeFloat()) - return airCmpBuiltinCall(f, inst, operator, operation); + return try cmpBuiltinCall(f, inst, operator, operation); 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 }); const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { // (A && B) || (C && (A == B)) // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload @@ -3580,8 +3716,9 @@ fn airEquality( try f.writeCValue(writer, lhs, .Other); try writer.writeAll(".is_null == "); try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(".is_null))"); - return; + try writer.writeAll(".is_null));\n"); + + return local; } try f.writeCValue(writer, lhs, .Other); @@ -3589,17 +3726,26 @@ fn airEquality( try writer.writeAll(operator); try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); + try writer.writeAll(";\n"); + + return local; } fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - const un_op = f.air.instructions.items(.data)[inst].un_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, operand, .Other); try writer.print(" < sizeof({ }) / sizeof(*{0 });\n", .{fmtIdent("zig_errorName")}); @@ -3607,16 +3753,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { } fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.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 }); - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); const elem_ty = switch (inst_ty.ptrSize()) { .One => blk: { const array_ty = inst_ty.childType(); @@ -3625,8 +3773,12 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { else => inst_ty.childType(), }; - // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer, - // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced. + // We must convert to and from integer types to prevent UB if the operation + // results in a NULL pointer, or if LHS is NULL. The operation is only UB + // if the result is NULL and then dereferenced. + const local = try f.allocLocal(inst, inst_ty); + const writer = f.object.writer(); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeAll(")(((uintptr_t)"); @@ -3642,23 +3794,30 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { return local; } -fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !void { +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 CValue.none; + } + const inst_ty = f.air.typeOfIndex(inst); const target = f.object.dg.module.getTarget(); if (inst_ty.isInt() and inst_ty.bitSize(target) > 64) - return airBinBuiltinCall(f, inst, operation[1..], .None); + return try airBinBuiltinCall(f, inst, operation[1..], .None); if (inst_ty.isRuntimeFloat()) - return airBinFloatOp(f, inst, operation); + return try airBinFloatOp(f, inst, operation); 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 }); const writer = f.object.writer(); - + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); // (lhs <> rhs) ? lhs : rhs - try writer.writeAll("("); + try writer.writeAll(" = ("); try f.writeCValue(writer, lhs, .Other); try writer.writeByte(' '); try writer.writeByte(operator); @@ -3668,28 +3827,38 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons try f.writeCValue(writer, lhs, .Other); try writer.writeAll(" : "); try f.writeCValue(writer, rhs, .Other); + try writer.writeAll(";\n"); + + return local; } fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.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 }); const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - - try writer.writeAll(" = {("); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".ptr = ("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); try writer.writeByte(')'); try f.writeCValue(writer, ptr, .Other); - try writer.writeAll(", "); + try writer.writeAll("; "); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".len = "); try f.writeCValue(writer, len, .Initializer); - try writer.writeAll("};\n"); + try writer.writeAll(";\n"); return local; } @@ -3701,6 +3870,7 @@ fn airCall( ) !CValue { // Not even allowed to call panic in a naked function. if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; + const gpa = f.object.dg.gpa; switch (modifier) { .auto => {}, @@ -3712,6 +3882,21 @@ fn airCall( const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); + + const resolved_args = try gpa.alloc(CValue, args.len); + defer gpa.free(resolved_args); + for (args) |arg, i| { + resolved_args[i] = try f.resolveInst(arg); + } + + const callee = try f.resolveInst(pl_op.operand); + + { + var bt = iterateBigTomb(f, inst); + try bt.feed(pl_op.operand); + for (args) |arg| try bt.feed(arg); + } + const callee_ty = f.air.typeOf(pl_op.operand); const fn_ty = switch (callee_ty.zigTypeTag()) { .Fn => callee_ty, @@ -3733,7 +3918,8 @@ fn airCall( try writer.writeByte(')'); break :r .none; } else r: { - const local = try f.allocLocal(lowered_ret_ty, .Const); + const local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); break :r local; }; @@ -3759,13 +3945,12 @@ fn airCall( break :callee; } // Fall back to function pointer call. - const callee = try f.resolveInst(pl_op.operand); try f.writeCValue(writer, callee, .Other); } try writer.writeByte('('); var args_written: usize = 0; - for (args) |arg| { + for (args) |arg, arg_i| { const ty = f.air.typeOf(arg); if (!ty.hasRuntimeBitsIgnoreComptime()) continue; if (args_written != 0) { @@ -3780,23 +3965,28 @@ fn airCall( if (ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); } - try f.writeCValue(writer, try f.resolveInst(arg), .FunctionArgument); + try f.writeCValue(writer, resolved_args[arg_i], .FunctionArgument); args_written += 1; } try writer.writeAll(");\n"); - if (result_local == .none or !lowersToArray(ret_ty, target)) return result_local; + const result = r: { + if (result_local == .none or !lowersToArray(ret_ty, target)) + break :r result_local; - const array_local = try f.allocLocal(ret_ty, .Mut); - try writer.writeAll(";\n"); - try writer.writeAll("memcpy("); - try f.writeCValue(writer, array_local, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValueMember(writer, result_local, .{ .field = 0 }); - try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, ret_ty); - try writer.writeAll("));\n"); - return array_local; + const array_local = try f.allocLocal(inst, ret_ty); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, array_local, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValueMember(writer, result_local, .{ .field = 0 }); + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, ret_ty); + try writer.writeAll("));\n"); + try freeLocal(f, inst, result_local.local, 0); + break :r array_local; + }; + + return result; } fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3828,6 +4018,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { const name = f.air.nullTerminatedString(pl_op.payload); const operand = try f.resolveInst(pl_op.operand); _ = operand; + try reap(f, inst, &.{pl_op.operand}); const writer = f.object.writer(); try writer.print("/* var:{s} */\n", .{name}); return CValue.none; @@ -3843,12 +4034,10 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: { - // allocate a location for the result - const local = try f.allocLocal(inst_ty, .Mut); - try writer.writeAll(";\n"); - break :blk local; - } else CValue{ .none = {} }; + const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) + try f.allocLocal(inst, inst_ty) + else + CValue{ .none = {} }; try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ .block_id = block_id, @@ -3864,32 +4053,30 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; - const err_union = try f.resolveInst(pl_op.operand); const extra = f.air.extraData(Air.Try, pl_op.payload); const body = f.air.extra[extra.end..][0..extra.data.body_len]; const err_union_ty = f.air.typeOf(pl_op.operand); - const result_ty = f.air.typeOfIndex(inst); - return lowerTry(f, err_union, body, err_union_ty, false, result_ty); + return lowerTry(f, inst, pl_op.operand, body, err_union_ty, false); } fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.TryPtr, ty_pl.payload); - const err_union_ptr = try f.resolveInst(extra.data.ptr); const body = f.air.extra[extra.end..][0..extra.data.body_len]; const err_union_ty = f.air.typeOf(extra.data.ptr).childType(); - const result_ty = f.air.typeOfIndex(inst); - return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty); + return lowerTry(f, inst, extra.data.ptr, body, err_union_ty, true); } fn lowerTry( f: *Function, - err_union: CValue, + inst: Air.Inst.Index, + operand: Air.Inst.Ref, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, - result_ty: Type, ) !CValue { + const err_union = try f.resolveInst(operand); + const result_ty = f.air.typeOfIndex(inst); const writer = f.object.writer(); const payload_ty = err_union_ty.errorUnionPayload(); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); @@ -3902,6 +4089,10 @@ fn lowerTry( else try f.writeCValue(writer, err_union, .Other); } else { + // Reap the operand so that it can be reused inside genBody. + // Remember we must avoid calling reap() twice for the same operand + // in this function. + try reap(f, inst, &.{operand}); if (operand_is_ptr or isByRef(err_union_ty)) try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "error" }) else @@ -3921,11 +4112,16 @@ fn lowerTry( } } + try reap(f, inst, &.{operand}); + + if (f.liveness.isUnused(inst)) { + return CValue.none; + } + const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(payload_ty, target); - const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const); + const local = try f.allocLocal(inst, result_ty); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); @@ -3934,6 +4130,7 @@ fn lowerTry( try f.renderTypecast(writer, payload_ty); try writer.writeAll("));\n"); } else { + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); if (operand_is_ptr or isByRef(payload_ty)) { try writer.writeByte('&'); @@ -3953,6 +4150,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { // If result is .none then the value of the block is unused. if (result != .none) { const operand = try f.resolveInst(branch.operand); + try reap(f, inst, &.{branch.operand}); const operand_ty = f.air.typeOf(branch.operand); const target = f.object.dg.module.getTarget(); @@ -3977,40 +4175,50 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { - const src_ty = f.air.typeOfIndex(inst); + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const dest_ty = f.air.typeOfIndex(inst); // No IgnoreComptime until Sema stops giving us garbage Air. // https://github.com/ziglang/zig/issues/13410 - if (f.liveness.isUnused(inst) or !src_ty.hasRuntimeBits()) return CValue.none; + if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } - const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const operand = try f.resolveInstNoInline(ty_op.operand); - const dest_ty = f.air.typeOf(ty_op.operand); + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + const operand_ty = f.air.typeOf(ty_op.operand); const target = f.object.dg.module.getTarget(); + const writer = f.object.writer(); - if (dest_ty.isAbiInt() and src_ty.isAbiInt()) { - const src_info = src_ty.intInfo(target); - const dest_info = dest_ty.intInfo(target); - if (std.meta.eql(src_info, dest_info)) { - return operand; + const local = try f.allocLocal(inst, dest_ty); + + if (operand_ty.isAbiInt() and dest_ty.isAbiInt()) { + const src_info = dest_ty.intInfo(target); + const dest_info = operand_ty.intInfo(target); + if (src_info.signedness == dest_info.signedness and + src_info.bits == dest_info.bits) + { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); + return local; } } - const writer = f.object.writer(); - if (src_ty.isPtrAtRuntime() and dest_ty.isPtrAtRuntime()) { - const local = try f.allocLocal(src_ty, .Const); + if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, src_ty); + try f.renderTypecast(writer, dest_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } - const local = try f.allocLocal(src_ty, .Mut); - try writer.writeAll(";\n"); - const operand_lval = if (operand == .constant) blk: { - const operand_local = try f.allocLocal(dest_ty, .Const); + const operand_local = try f.allocLocal(inst, operand_ty); + try f.writeCValue(writer, operand_local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, operand, .Initializer); try writer.writeAll(";\n"); @@ -4022,20 +4230,24 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", &"); try f.writeCValue(writer, operand_lval, .Other); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, src_ty); + try f.renderTypecast(writer, dest_ty); try writer.writeAll("));\n"); // Ensure padding bits have the expected value. - if (src_ty.isAbiInt()) { + if (dest_ty.isAbiInt()) { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, src_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty); try writer.writeByte('('); try f.writeCValue(writer, local, .Other); - try f.object.dg.renderBuiltinInfo(writer, src_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits); try writer.writeAll(");\n"); } + if (operand == .constant) { + try freeLocal(f, inst, operand_lval.local, 0); + } + return local; } @@ -4047,7 +4259,8 @@ fn airBreakpoint(writer: anytype) !CValue { fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const writer = f.object.writer(); - const local = try f.allocLocal(Type.usize, .Const); + const local = try f.allocLocal(inst, Type.usize); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, Type.usize); try writer.writeAll(")zig_return_address();\n"); @@ -4057,7 +4270,8 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const writer = f.object.writer(); - const local = try f.allocLocal(Type.usize, .Const); + const local = try f.allocLocal(inst, Type.usize); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, Type.usize); try writer.writeAll(")zig_frame_address();\n"); @@ -4088,27 +4302,79 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { const loop = f.air.extraData(Air.Block, ty_pl.payload); const body = f.air.extra[loop.end..][0..loop.data.body_len]; const writer = f.object.writer(); - try writer.writeAll("while ("); - try f.object.dg.renderValue(writer, Type.bool, Value.true, .condition); - try writer.writeAll(") "); + + const gpa = f.object.dg.gpa; + try f.free_locals_stack.insert(gpa, f.free_locals_stack.items.len - 1, .{}); + + try writer.writeAll("for (;;) "); try genBody(f, body); try writer.writeByte('\n'); + + var old_free_locals = f.free_locals_stack.pop(); + defer deinitFreeLocalsMap(gpa, &old_free_locals); + const new_free_locals = f.getFreeLocals(); + var it = new_free_locals.iterator(); + while (it.next()) |entry| { + const gop = try old_free_locals.getOrPutContext(gpa, entry.key_ptr.*, f.tyHashCtx()); + if (gop.found_existing) { + try gop.value_ptr.appendSlice(gpa, entry.value_ptr.items); + } else { + gop.value_ptr.* = entry.value_ptr.*; + entry.value_ptr.* = .{}; + } + } + deinitFreeLocalsMap(gpa, new_free_locals); + new_free_locals.* = old_free_locals.move(); + return CValue.none; } fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; const cond = try f.resolveInst(pl_op.operand); + try reap(f, inst, &.{pl_op.operand}); const extra = f.air.extraData(Air.CondBr, pl_op.payload); const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len]; const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + 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.getFreeLocals()); + 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); + const pre_clone_depth = f.free_locals_clone_depth; + f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); + + for (liveness_condbr.then_deaths) |operand| { + try die(f, inst, Air.indexToRef(operand)); + } + try writer.writeAll("if ("); - try f.writeCValue(writer, cond, .condition); + try f.writeCValue(writer, cond, .Other); try writer.writeAll(") "); try genBody(f, then_body); try writer.writeAll(" else "); + f.value_map.deinit(); + f.value_map = cloned_map.move(); + const free_locals = f.getFreeLocals(); + deinitFreeLocalsMap(gpa, free_locals); + free_locals.* = cloned_frees.move(); + f.free_locals_clone_depth = pre_clone_depth; + for (liveness_condbr.else_deaths) |operand| { + try die(f, inst, Air.indexToRef(operand)); + } + + try noticeBranchFrees(f, pre_locals_len, inst); + try genBody(f, else_body); try f.object.indent_writer.insertNewline(); @@ -4118,6 +4384,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; const condition = try f.resolveInst(pl_op.operand); + try reap(f, inst, &.{pl_op.operand}); const condition_ty = f.air.typeOf(pl_op.operand); const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload); const writer = f.object.writer(); @@ -4136,6 +4403,15 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(") {"); f.object.indent_writer.pushIndent(); + const gpa = f.object.dg.gpa; + 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. + const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); + var extra_index: usize = switch_br.end; var case_i: u32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { @@ -4155,14 +4431,61 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); try writer.writeAll(": "); } + + 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.getFreeLocals(); + 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); + const pre_clone_depth = f.free_locals_clone_depth; + f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); + + { + defer { + f.free_locals_clone_depth = pre_clone_depth; + f.value_map.deinit(); + free_locals = f.getFreeLocals(); + 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, inst); + } else { + for (liveness.deaths[case_i]) |operand| { + try die(f, inst, Air.indexToRef(operand)); + } + try genBody(f, case_body); + } + // The case body must be noreturn so we don't need to insert a break. - try genBody(f, case_body); + } const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len]; try f.object.indent_writer.insertNewline(); - try writer.writeAll("default: "); - try genBody(f, else_body); + if (else_body.len > 0) { + for (liveness.deaths[liveness.deaths.len - 1]) |operand| { + try die(f, inst, Air.indexToRef(operand)); + } + try writer.writeAll("default: "); + try genBody(f, else_body); + } else { + try writer.writeAll("default: zig_unreachable();"); + } try f.object.indent_writer.insertNewline(); f.object.indent_writer.popIndent(); @@ -4189,235 +4512,268 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; - if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; - - const writer = f.object.writer(); - const inst_ty = f.air.typeOfIndex(inst); - const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { - const local = try f.allocLocal(inst_ty, .Mut); - if (f.wantSafety()) { - try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); - } - try writer.writeAll(";\n"); - break :local local; - } else .none; - - const locals_begin = f.next_local_index; - const constraints_extra_begin = extra_i; - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint.len < 2 or constraint[0] != '=' or - (constraint[1] == '{' and constraint[constraint.len - 1] != '}')) - { - return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); - } + const result: CValue = r: { + if (!is_volatile and f.liveness.isUnused(inst)) break :r CValue.none; - const is_reg = constraint[1] == '{'; - if (is_reg) { - const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); - try writer.writeAll("register "); - _ = try f.allocLocal(output_ty, .Mut); - try writer.writeAll(" __asm(\""); - try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); - try writer.writeAll("\")"); + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { + const local = try f.allocLocal(inst, inst_ty); if (f.wantSafety()) { + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer); + try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); + try writer.writeAll(";\n"); } - try writer.writeAll(";\n"); - } - } - for (inputs) |input| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or - (constraint[0] == '{' and constraint[constraint.len - 1] != '}')) - { - return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); - } + break :local local; + } else .none; + + const locals_begin = @intCast(LocalIndex, f.locals.items.len); + const constraints_extra_begin = extra_i; + for (outputs) |output| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; - const is_reg = constraint[0] == '{'; - const input_val = try f.resolveInst(input); - if (asmInputNeedsLocal(constraint, input_val)) { - const input_ty = f.air.typeOf(input); - if (is_reg) try writer.writeAll("register "); - _ = try f.allocLocal(input_ty, .Const); + if (constraint.len < 2 or constraint[0] != '=' or + (constraint[1] == '{' and constraint[constraint.len - 1] != '}')) + { + return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + } + + const is_reg = constraint[1] == '{'; if (is_reg) { + const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); + try writer.writeAll("register "); + const alignment = 0; + const local_value = try f.allocLocalValue(output_ty, alignment); + try f.object.dg.renderTypeAndName( + writer, + output_ty, + local_value, + .Mut, + alignment, + .Complete, + ); try writer.writeAll(" __asm(\""); - try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); + try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); + if (f.wantSafety()) { + try writer.writeAll(" = "); + try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer); + } + try writer.writeAll(";\n"); } - try writer.writeAll(" = "); - try f.writeCValue(writer, input_val, .Initializer); - try writer.writeAll(";\n"); } - } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + for (inputs) |input| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - } - } - { - const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; - var stack = std.heap.stackFallback(256, f.object.dg.gpa); - const allocator = stack.get(); - const fixed_asm_source = try allocator.alloc(u8, asm_source.len); - defer allocator.free(fixed_asm_source); + if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or + (constraint[0] == '{' and constraint[constraint.len - 1] != '}')) + { + return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + } - var src_i: usize = 0; - var dst_i: usize = 0; - while (true) { - const literal = mem.sliceTo(asm_source[src_i..], '%'); - src_i += literal.len; + const is_reg = constraint[0] == '{'; + const input_val = try f.resolveInst(input); + if (asmInputNeedsLocal(constraint, input_val)) { + const input_ty = f.air.typeOf(input); + if (is_reg) try writer.writeAll("register "); + const alignment = 0; + const local_value = try f.allocLocalValue(input_ty, alignment); + try f.object.dg.renderTypeAndName( + writer, + input_ty, + local_value, + .Const, + alignment, + .Complete, + ); + if (is_reg) { + try writer.writeAll(" __asm(\""); + try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); + try writer.writeAll("\")"); + } + try writer.writeAll(" = "); + try f.writeCValue(writer, input_val, .Initializer); + try writer.writeAll(";\n"); + } + } + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + } + } + + { + const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; - mem.copy(u8, fixed_asm_source[dst_i..], literal); - dst_i += literal.len; + var stack = std.heap.stackFallback(256, f.object.dg.gpa); + const allocator = stack.get(); + const fixed_asm_source = try allocator.alloc(u8, asm_source.len); + defer allocator.free(fixed_asm_source); - if (src_i >= asm_source.len) break; + var src_i: usize = 0; + var dst_i: usize = 0; + while (true) { + const literal = mem.sliceTo(asm_source[src_i..], '%'); + src_i += literal.len; - src_i += 1; - if (src_i >= asm_source.len) - return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); + mem.copy(u8, fixed_asm_source[dst_i..], literal); + dst_i += literal.len; - fixed_asm_source[dst_i] = '%'; - dst_i += 1; + if (src_i >= asm_source.len) break; - if (asm_source[src_i] != '[') { - // This also handles %% - fixed_asm_source[dst_i] = asm_source[src_i]; src_i += 1; + if (src_i >= asm_source.len) + return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); + + fixed_asm_source[dst_i] = '%'; dst_i += 1; - continue; - } - const desc = mem.sliceTo(asm_source[src_i..], ']'); - if (mem.indexOfScalar(u8, desc, ':')) |colon| { - const name = desc[0..colon]; - const modifier = desc[colon + 1 ..]; + if (asm_source[src_i] != '[') { + // This also handles %% + fixed_asm_source[dst_i] = asm_source[src_i]; + src_i += 1; + dst_i += 1; + continue; + } - mem.copy(u8, fixed_asm_source[dst_i..], modifier); - dst_i += modifier.len; - mem.copy(u8, fixed_asm_source[dst_i..], name); - dst_i += name.len; + const desc = mem.sliceTo(asm_source[src_i..], ']'); + if (mem.indexOfScalar(u8, desc, ':')) |colon| { + const name = desc[0..colon]; + const modifier = desc[colon + 1 ..]; - src_i += desc.len; - if (src_i >= asm_source.len) - return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); + mem.copy(u8, fixed_asm_source[dst_i..], modifier); + dst_i += modifier.len; + mem.copy(u8, fixed_asm_source[dst_i..], name); + dst_i += name.len; + + src_i += desc.len; + if (src_i >= asm_source.len) + return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); + } } + + try writer.writeAll("__asm"); + if (is_volatile) try writer.writeAll(" volatile"); + try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); } - try writer.writeAll("__asm"); - if (is_volatile) try writer.writeAll(" volatile"); - try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); - } - - extra_i = constraints_extra_begin; - var locals_index = locals_begin; - try writer.writeByte(':'); - for (outputs) |output, index| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (index > 0) try writer.writeByte(','); - try writer.writeByte(' '); - if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); - const is_reg = constraint[1] == '{'; - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); - if (is_reg) { - try f.writeCValue(writer, .{ .local = locals_index }, .Other); - locals_index += 1; - } else if (output == .none) { - try f.writeCValue(writer, local, .FunctionArgument); - } else { - try f.writeCValueDeref(writer, try f.resolveInst(output)); + extra_i = constraints_extra_begin; + var locals_index = locals_begin; + try writer.writeByte(':'); + for (outputs) |output, index| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (index > 0) try writer.writeByte(','); + try writer.writeByte(' '); + if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); + const is_reg = constraint[1] == '{'; + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); + if (is_reg) { + try f.writeCValue(writer, .{ .local = locals_index }, .Other); + locals_index += 1; + } else if (output == .none) { + try f.writeCValue(writer, local, .FunctionArgument); + } else { + try f.writeCValueDeref(writer, try f.resolveInst(output)); + } + try writer.writeByte(')'); } - try writer.writeByte(')'); - } - try writer.writeByte(':'); - for (inputs) |input, index| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (index > 0) try writer.writeByte(','); - try writer.writeByte(' '); - if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); - - const is_reg = constraint[0] == '{'; - const input_val = try f.resolveInst(input); - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); - try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { - const input_local = CValue{ .local = locals_index }; - locals_index += 1; - break :local input_local; - } else input_val, .Other); - try writer.writeByte(')'); - } - try writer.writeByte(':'); - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + try writer.writeByte(':'); + for (inputs) |input, index| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (index > 0) try writer.writeByte(','); + try writer.writeByte(' '); + if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); + + const is_reg = constraint[0] == '{'; + const input_val = try f.resolveInst(input); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); + try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { + const input_local = CValue{ .local = locals_index }; + locals_index += 1; + break :local input_local; + } else input_val, .Other); + try writer.writeByte(')'); + } + try writer.writeByte(':'); + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; - if (clobber.len == 0) continue; + if (clobber.len == 0) continue; - if (clobber_i > 0) try writer.writeByte(','); - try writer.print(" {s}", .{fmtStringLiteral(clobber)}); + if (clobber_i > 0) try writer.writeByte(','); + try writer.print(" {s}", .{fmtStringLiteral(clobber)}); + } } - } - try writer.writeAll(");\n"); + try writer.writeAll(");\n"); - extra_i = constraints_extra_begin; - locals_index = locals_begin; - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - const is_reg = constraint[1] == '{'; - if (is_reg) { - try f.writeCValueDeref(writer, if (output == .none) - CValue{ .local_ref = local.local } - else - try f.resolveInst(output)); - try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .local = locals_index }, .Other); - locals_index += 1; - try writer.writeAll(";\n"); + extra_i = constraints_extra_begin; + locals_index = locals_begin; + for (outputs) |output| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + const is_reg = constraint[1] == '{'; + if (is_reg) { + try f.writeCValueDeref(writer, if (output == .none) + CValue{ .local_ref = local.local } + else + try f.resolveInst(output)); + try writer.writeAll(" = "); + try f.writeCValue(writer, .{ .local = locals_index }, .Other); + locals_index += 1; + try writer.writeAll(";\n"); + } } + + break :r local; + }; + + var bt = iterateBigTomb(f, inst); + for (outputs) |output| { + if (output == .none) continue; + try bt.feed(output); + } + for (inputs) |input| { + try bt.feed(input); } - return local; + return result; } fn airIsNull( @@ -4425,12 +4781,26 @@ fn airIsNull( inst: Air.Inst.Index, operator: []const u8, is_ptr: bool, -) !void { +) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const writer = f.object.writer(); const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); - try if (is_ptr) f.writeCValueDeref(writer, operand) else f.writeCValue(writer, operand, .Other); + const local = try f.allocLocal(inst, Type.bool); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + if (is_ptr) { + try f.writeCValueDeref(writer, operand); + } else { + try f.writeCValue(writer, operand, .Other); + } const operand_ty = f.air.typeOf(un_op); const optional_ty = if (is_ptr) operand_ty.childType() else operand_ty; @@ -4457,33 +4827,52 @@ fn airIsNull( try writer.writeAll(operator); try writer.writeByte(' '); try f.object.dg.renderValue(writer, rhs.ty, rhs.val, .Other); + try writer.writeAll(";\n"); + return local; } fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.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); var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none; - if (opt_ty.optionalReprIsPayload()) return operand; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); + const local = try f.allocLocal(inst, inst_ty); + const writer = f.object.writer(); + + if (opt_ty.optionalReprIsPayload()) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); + return local; + } + const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(inst_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); - const writer = f.object.writer(); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else try writer.writeAll(" = "); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); if (is_array) { try writer.writeAll(", sizeof("); @@ -4495,11 +4884,16 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { } fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } + const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const ptr_ty = f.air.typeOf(ty_op.operand); const opt_ty = ptr_ty.childType(); const inst_ty = f.air.typeOfIndex(inst); @@ -4508,15 +4902,18 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue{ .undef = inst_ty }; } + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + if (opt_ty.optionalReprIsPayload()) { // the operand is just a regular pointer, no need to do anything special. // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C - return operand; + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + } else { + try writer.writeAll(" = &"); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); } - - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = &"); - try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); try writer.writeAll(";\n"); return local; } @@ -4525,62 +4922,88 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.air.typeOf(ty_op.operand); const opt_ty = operand_ty.elemType(); + const inst_ty = f.air.typeOfIndex(inst); + if (opt_ty.optionalReprIsPayload()) { + if (f.liveness.isUnused(inst)) { + return CValue.none; + } + const local = try f.allocLocal(inst, inst_ty); // The payload and the optional are the same value. // Setting to non-null will be done when the payload is set. - return operand; - } - - try f.writeCValueDeref(writer, operand); - try writer.writeAll(".is_null = "); - try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer); - try writer.writeAll(";\n"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); + return local; + } else { + try f.writeCValueDeref(writer, operand); + try writer.writeAll(".is_null = "); + try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer); + try writer.writeAll(";\n"); - const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = &"); - try f.writeCValueDeref(writer, operand); + if (f.liveness.isUnused(inst)) { + return CValue.none; + } - try writer.writeAll(".payload;\n"); - return local; + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = &"); + try f.writeCValueDeref(writer, operand); + try writer.writeAll(".payload;\n"); + return local; + } } fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) + 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}); // TODO this @as is needed because of a stage1 bug return @as(CValue, CValue.none); + } - const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; - const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; const struct_ptr = try f.resolveInst(extra.struct_operand); + try reap(f, inst, &.{extra.struct_operand}); const struct_ptr_ty = f.air.typeOf(extra.struct_operand); return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index); } fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { - if (f.liveness.isUnused(inst)) + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); // TODO this @as is needed because of a stage1 bug return @as(CValue, CValue.none); + } - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const struct_ptr = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const struct_ptr_ty = f.air.typeOf(ty_op.operand); return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index); } fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.none; + } + const struct_ptr_ty = f.air.typeOfIndex(inst); const field_ptr_ty = f.air.typeOf(extra.field_ptr); const field_ptr_val = try f.resolveInst(extra.field_ptr); + try reap(f, inst, &.{extra.field_ptr}); const target = f.object.dg.module.getTarget(); const struct_ty = struct_ptr_ty.childType(); @@ -4597,7 +5020,8 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); const writer = f.object.writer(); - const local = try f.allocLocal(struct_ptr_ty, .Const); + const local = try f.allocLocal(inst, struct_ptr_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, struct_ptr_ty); try writer.writeAll(")&(("); @@ -4618,7 +5042,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc // Ensure complete type definition is visible before accessing fields. try f.renderType(std.io.null_writer, struct_ty); - const local = try f.allocLocal(field_ptr_ty, .Const); + const local = try f.allocLocal(inst, field_ptr_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); try f.renderTypecast(writer, field_ptr_ty); try writer.writeByte(')'); @@ -4706,16 +5131,23 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc } fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) + 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 CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - if (!inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none; + if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { + try reap(f, inst, &.{extra.struct_operand}); + return CValue.none; + } - const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; - const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; const target = f.object.dg.module.getTarget(); const struct_byval = try f.resolveInst(extra.struct_operand); + try reap(f, inst, &.{extra.struct_operand}); const struct_ty = f.air.typeOf(extra.struct_operand); const writer = f.object.writer(); @@ -4759,7 +5191,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const field_int_ty = Type.initPayload(&field_int_pl.base); - const temp_local = try f.allocLocal(field_int_ty, .Const); + const temp_local = try f.allocLocal(inst, try field_int_ty.copy(f.arena.allocator())); + try f.writeCValue(writer, temp_local, .Other); try writer.writeAll(" = zig_wrap_"); try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); try writer.writeAll("(("); @@ -4775,8 +5208,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(");\n"); if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; - const local = try f.allocLocal(inst_ty, .Mut); - try writer.writeAll(";\n"); + const local = try f.allocLocal(inst, inst_ty); try writer.writeAll("memcpy("); try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument); try writer.writeAll(", "); @@ -4784,20 +5216,21 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", sizeof("); try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); + try freeLocal(f, inst, temp_local.local, 0); return local; }, }, .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { const operand_lval = if (struct_byval == .constant) blk: { - const operand_local = try f.allocLocal(struct_ty, .Const); + const operand_local = try f.allocLocal(inst, struct_ty); + try f.writeCValue(writer, operand_local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, struct_byval, .Initializer); try writer.writeAll(";\n"); break :blk operand_local; } else struct_byval; - const local = try f.allocLocal(inst_ty, .Mut); - try writer.writeAll(";\n"); + const local = try f.allocLocal(inst, inst_ty); try writer.writeAll("memcpy(&"); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", &"); @@ -4805,6 +5238,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", sizeof("); try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); + + if (struct_byval == .constant) { + try freeLocal(f, inst, operand_lval.local, 0); + } + return local; } else .{ .identifier = struct_ty.unionFields().keys()[extra.field_index], @@ -4822,13 +5260,15 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const is_array = lowersToArray(inst_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + const local = try f.allocLocal(inst, inst_ty); if (is_array) { - try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else try writer.writeAll(" = "); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + } if (extra_name != .none) { try f.writeCValueMember(writer, struct_byval, extra_name); try writer.writeByte('.'); @@ -4846,40 +5286,53 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { /// *(E!T) -> E /// Note that the result is never a pointer. fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.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); + try reap(f, inst, &.{ty_op.operand}); const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) return operand; - + const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); - if (!error_ty.errorSetIsEmpty()) - if (operand_is_ptr) - try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) + + if (!payload_ty.hasRuntimeBits()) { + try f.writeCValue(writer, operand, .Other); + } else { + if (!error_ty.errorSetIsEmpty()) + if (operand_is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) else - try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) - else - try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer); + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer); + } try writer.writeAll(";\n"); return local; } fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { - if (f.liveness.isUnused(inst)) + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); return CValue.none; + } - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.air.typeOf(ty_op.operand); const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; @@ -4887,8 +5340,9 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { if (!is_ptr) return CValue.none; - const local = try f.allocLocal(inst_ty, .Const); const w = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); try f.renderTypecast(w, inst_ty); try w.writeByte(')'); @@ -4898,7 +5352,8 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu } const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); if (is_ptr) try writer.writeByte('&'); if (operand_is_ptr) @@ -4910,24 +5365,40 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu } fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const payload = try f.resolveInst(ty_op.operand); - if (inst_ty.optionalReprIsPayload()) return payload; + try reap(f, inst, &.{ty_op.operand}); + const writer = f.object.writer(); + + if (inst_ty.optionalReprIsPayload()) { + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, payload, .Other); + try writer.writeAll(";\n"); + return local; + } const payload_ty = f.air.typeOf(ty_op.operand); const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(payload_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); - const writer = f.object.writer(); - try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer); - try writer.writeAll(", .is_null = "); - try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer); - try writer.writeAll(" };\n"); + const local = try f.allocLocal(inst, inst_ty); + if (!is_array) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".payload = "); + try f.writeCValue(writer, payload, .Other); + try writer.writeAll("; "); + } + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".is_null = false;\n"); if (is_array) { try writer.writeAll("memcpy("); try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); @@ -4941,21 +5412,35 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { } fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const writer = f.object.writer(); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const error_union_ty = f.air.typeOfIndex(inst); const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) return operand; - - const local = try f.allocLocal(error_union_ty, .Const); - try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, .{ .undef = payload_ty }, .Initializer); - try writer.writeAll(", .error = "); - try f.writeCValue(writer, operand, .Initializer); - try writer.writeAll(" };\n"); + const local = try f.allocLocal(inst, error_union_ty); + + if (!payload_ty.hasRuntimeBits()) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); + return local; + } + + { + // TODO: set the payload to undefined + //try f.writeCValue(writer, local, .Other); + } + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".error = "); + try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); return local; } @@ -4977,6 +5462,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { return operand; } + try reap(f, inst, &.{ty_op.operand}); try f.writeCValueDeref(writer, operand); try writer.writeAll(".error = "); try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); @@ -4985,7 +5471,8 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { // Then return the payload pointer (only if it is used) if (f.liveness.isUnused(inst)) return CValue.none; - const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); + const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = &("); try f.writeCValueDeref(writer, operand); try writer.writeAll(").payload;\n"); @@ -5008,24 +5495,30 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue { } fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const error_ty = inst_ty.errorUnionSet(); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const payload_ty = inst_ty.errorUnionPayload(); const payload = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const target = f.object.dg.module.getTarget(); const is_array = lowersToArray(payload_ty, target); - const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); const writer = f.object.writer(); - try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer); - try writer.writeAll(", .error = "); - try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer); - try writer.writeAll(" };\n"); + const local = try f.allocLocal(inst, inst_ty); + if (!is_array) { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".payload = "); + try f.writeCValue(writer, payload, .Other); + try writer.writeAll("; "); + } + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".error = 0;\n"); if (is_array) { try writer.writeAll("memcpy("); try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); @@ -5038,15 +5531,26 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !void { +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 CValue.none; + } + const writer = f.object.writer(); const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const operand_ty = f.air.typeOf(un_op); + const local = try f.allocLocal(inst, Type.bool); const err_union_ty = if (is_ptr) operand_ty.childType() else operand_ty; const payload_ty = err_union_ty.errorUnionPayload(); const error_ty = err_union_ty.errorUnionSet(); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + if (!error_ty.errorSetIsEmpty()) if (payload_ty.hasRuntimeBits()) if (is_ptr) @@ -5061,20 +5565,27 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const try writer.writeAll(operator); try writer.writeByte(' '); try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); + try writer.writeAll(";\n"); + return local; } fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); return CValue.none; + } + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const writer = f.object.writer(); - const operand = try f.resolveInst(ty_op.operand); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen(); - try writer.writeAll(" = { .ptr = "); + try writer.writeAll(".ptr = "); if (operand == .undef) { // Unfortunately, C does not support any equivalent to // &(*(void *)p)[0], although LLVM does via GetElementPtr @@ -5088,16 +5599,23 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; const len_val = Value.initPayload(&len_pl.base); - try writer.print(", .len = {} }};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); + try writer.writeAll("; "); + try f.writeCValue(writer, local, .Other); + try writer.print(".len = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); return local; } fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.air.typeOf(ty_op.operand); const target = f.object.dg.module.getTarget(); const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat()) @@ -5109,8 +5627,9 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { else unreachable; - const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { @@ -5134,16 +5653,27 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !void { - const inst_ty = f.air.typeOfIndex(inst); +fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; - const writer = f.object.writer(); + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + const inst_ty = f.air.typeOfIndex(inst); + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); - try writer.writeAll("("); + try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); + try writer.writeAll(";\n"); + return local; } fn airUnBuiltinCall( @@ -5151,19 +5681,31 @@ fn airUnBuiltinCall( inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo, -) !void { - const operand = f.air.instructions.items(.data)[inst].ty_op.operand; - const operand_ty = f.air.typeOf(operand); +) !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 CValue.none; + } + + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + const inst_ty = f.air.typeOfIndex(inst); + const operand_ty = f.air.typeOf(ty_op.operand); const writer = f.object.writer(); - try writer.writeAll("zig_"); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = zig_"); try writer.writeAll(operation); try writer.writeByte('_'); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); - try f.writeCValue(writer, try f.resolveInst(operand), .FunctionArgument); + try f.writeCValue(writer, operand, .FunctionArgument); try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); - try writer.writeAll(")"); + try writer.writeAll(");\n"); + return local; } fn airBinBuiltinCall( @@ -5171,99 +5713,131 @@ fn airBinBuiltinCall( inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo, -) !void { +) !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 CValue.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 }); + + const inst_ty = f.air.typeOfIndex(inst); const operand_ty = f.air.typeOf(bin_op.lhs); const writer = f.object.writer(); - try writer.writeAll("zig_"); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = zig_"); try writer.writeAll(operation); try writer.writeByte('_'); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); - try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); + try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); + try f.writeCValue(writer, rhs, .FunctionArgument); try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); - try writer.writeAll(")"); + try writer.writeAll(");\n"); + return local; } -fn airCmpBuiltinCall( +fn cmpBuiltinCall( f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8, -) !void { +) !CValue { + const inst_ty = f.air.typeOfIndex(inst); const bin_op = f.air.instructions.items(.data)[inst].bin_op; const operand_ty = f.air.typeOf(bin_op.lhs); + 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 }); + const writer = f.object.writer(); - try writer.writeAll("zig_"); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = zig_"); try writer.writeAll(operation); try writer.writeByte('_'); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); - try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); + try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); - try writer.print(") {s} {}", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) }); + try f.writeCValue(writer, rhs, .FunctionArgument); + try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) }); + return local; } fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; const inst_ty = f.air.typeOfIndex(inst); - const is_struct = !inst_ty.isPtrLikeOptional(); - const ptr_ty = f.air.typeOf(extra.ptr); const ptr = try f.resolveInst(extra.ptr); const expected_value = try f.resolveInst(extra.expected_value); const new_value = try f.resolveInst(extra.new_value); + try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); const writer = f.object.writer(); - - const local = try f.allocLocal(inst_ty, .Mut); - try writer.writeAll(" = "); - if (is_struct) try writer.writeAll("{ .payload = "); - try f.writeCValue(writer, expected_value, .Initializer); - if (is_struct) { - try writer.writeAll(", .is_null = "); - try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer); - try writer.writeAll(" }"); - } - try writer.writeAll(";\n"); - - if (is_struct) { + const ptr_ty = f.air.typeOf(extra.ptr); + const local = try f.allocLocal(inst, inst_ty); + if (inst_ty.isPtrLikeOptional()) { try f.writeCValue(writer, local, .Other); - try writer.writeAll(".is_null = "); - } else { + try writer.writeAll(" = "); + try f.writeCValue(writer, expected_value, .Initializer); + try writer.writeAll(";\n"); try writer.writeAll("if ("); - } - try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderTypecast(writer, ptr_ty.elemType()); - try writer.writeByte(')'); - if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); - try writer.writeAll(" *)"); - try f.writeCValue(writer, ptr, .Other); - try writer.writeAll(", "); - if (is_struct) - try f.writeCValueMember(writer, local, .{ .identifier = "payload" }) - else + try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); + try f.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); + try f.writeCValue(writer, ptr, .Other); + try writer.writeAll(", "); try f.writeCValue(writer, local, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValue(writer, new_value, .FunctionArgument); - try writer.writeAll(", "); - try writeMemoryOrder(writer, extra.successOrder()); - try writer.writeAll(", "); - try writeMemoryOrder(writer, extra.failureOrder()); - try writer.writeByte(')'); - if (is_struct) { - try writer.writeAll(";\n"); - } else { + try writer.writeAll(", "); + try f.writeCValue(writer, new_value, .FunctionArgument); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.successOrder()); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.failureOrder()); + try writer.writeByte(')'); try writer.writeAll(") {\n"); f.object.indent_writer.pushIndent(); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = NULL;\n"); f.object.indent_writer.popIndent(); try writer.writeAll("}\n"); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(".payload = "); + try f.writeCValue(writer, expected_value, .Other); + try writer.writeAll(";\n"); + try f.writeCValue(writer, local, .Other); + try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); + try f.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); + try f.writeCValue(writer, ptr, .Other); + try writer.writeAll(", "); + try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); + try writer.writeAll(", "); + try f.writeCValue(writer, new_value, .FunctionArgument); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.successOrder()); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.failureOrder()); + try writer.writeByte(')'); + try writer.writeAll(";\n"); + } + + if (f.liveness.isUnused(inst)) { + try freeLocal(f, inst, local.local, 0); + return CValue.none; } return local; @@ -5276,8 +5850,10 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { const ptr_ty = f.air.typeOf(pl_op.operand); const ptr = try f.resolveInst(pl_op.operand); const operand = try f.resolveInst(extra.operand); - const local = try f.allocLocal(inst_ty, .Const); + try reap(f, inst, &.{ pl_op.operand, extra.operand }); const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.print(" = zig_atomicrmw_{s}((", .{toAtomicRmwSuffix(extra.op())}); switch (extra.op()) { @@ -5300,19 +5876,27 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { try writeMemoryOrder(writer, extra.ordering()); try writer.writeAll(");\n"); + if (f.liveness.isUnused(inst)) { + try freeLocal(f, inst, local.local, 0); + return CValue.none; + } + return local; } fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; 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)) + if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_atomic_load((zig_atomic("); try f.renderTypecast(writer, ptr_ty.elemType()); @@ -5332,6 +5916,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const ptr_ty = f.air.typeOf(bin_op.lhs); const ptr = try f.resolveInst(bin_op.lhs); const element = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const writer = f.object.writer(); try writer.writeAll("zig_atomic_store((zig_atomic("); @@ -5354,6 +5939,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { const dest_ptr = try f.resolveInst(pl_op.operand); const value = try f.resolveInst(extra.lhs); const len = try f.resolveInst(extra.rhs); + try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); const writer = f.object.writer(); if (dest_ty.isVolatilePtr()) { @@ -5362,7 +5948,8 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); try writer.writeAll("for ("); - const index = try f.allocLocal(Type.usize, .Mut); + const index = try f.allocLocal(inst, Type.usize); + try f.writeCValue(writer, index, .Other); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, Type.usize, Value.zero, .Initializer); try writer.writeAll("; "); @@ -5383,6 +5970,8 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, value, .FunctionArgument); try writer.writeAll(";\n"); + try freeLocal(f, inst, index.local, 0); + return CValue.none; } @@ -5403,6 +5992,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { const dest_ptr = try f.resolveInst(pl_op.operand); const src_ptr = try f.resolveInst(extra.lhs); const len = try f.resolveInst(extra.rhs); + try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); const writer = f.object.writer(); try writer.writeAll("memcpy("); @@ -5420,6 +6010,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const union_ptr = try f.resolveInst(bin_op.lhs); const new_tag = try f.resolveInst(bin_op.rhs); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const writer = f.object.writer(); const union_ty = f.air.typeOf(bin_op.lhs).childType(); @@ -5436,53 +6027,94 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } -fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !void { +fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const un_ty = f.air.typeOf(ty_op.operand); - const writer = f.object.writer(); + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } + const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const un_ty = f.air.typeOf(ty_op.operand); const target = f.object.dg.module.getTarget(); const layout = un_ty.unionGetLayout(target); - assert(layout.tag_size != 0); + if (layout.tag_size == 0) return CValue.none; + const inst_ty = f.air.typeOfIndex(inst); + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + + try writer.writeAll(" = "); try f.writeCValue(writer, operand, .Other); - try writer.writeAll(".tag"); + try writer.writeAll(".tag;\n"); + return local; } -fn airTagName(f: *Function, inst: Air.Inst.Index) !void { +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 CValue.none; + } + + const inst_ty = f.air.typeOfIndex(inst); const enum_ty = f.air.typeOf(un_op); const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const writer = f.object.writer(); - try writer.print("{s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); try f.writeCValue(writer, operand, .Other); - try writer.writeAll(")"); + try writer.writeAll(");\n"); + + return local; } -fn airErrorName(f: *Function, inst: Air.Inst.Index) !void { +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 CValue.none; + } + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); - try writer.writeAll("zig_errorName["); + try writer.writeAll(" = zig_errorName["); try f.writeCValue(writer, operand, .Other); - try writer.writeAll("]"); + try writer.writeAll("];\n"); + return local; } fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const ty_op = f.air.instructions.items(.data)[inst].ty_op; const operand = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); _ = operand; - _ = local; return f.fail("TODO: C backend: implement airSplat", .{}); } @@ -5499,12 +6131,17 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { } fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + const reduce = f.air.instructions.items(.data)[inst].reduce; + + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{reduce.operand}); + return CValue.none; + } const target = f.object.dg.module.getTarget(); const scalar_ty = f.air.typeOfIndex(inst); - const reduce = f.air.instructions.items(.data)[inst].reduce; const operand = try f.resolveInst(reduce.operand); + try reap(f, inst, &.{reduce.operand}); const operand_ty = f.air.typeOf(reduce.operand); const vector_len = operand_ty.vectorLen(); const writer = f.object.writer(); @@ -5581,10 +6218,12 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { // } // break :reduce accum; // } - const it = try f.allocLocal(Type.usize, .Mut); + const it = try f.allocLocal(inst, Type.usize); + try f.writeCValue(writer, it, .Other); try writer.writeAll(" = 0;\n"); - const accum = try f.allocLocal(scalar_ty, .Mut); + const accum = try f.allocLocal(inst, scalar_ty); + try f.writeCValue(writer, accum, .Other); try writer.writeAll(" = "); const init_val = switch (reduce.operation) { @@ -5604,7 +6243,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(init_val); try writer.writeAll(";"); try f.object.indent_writer.insertNewline(); - try writer.writeAll("for(;"); + try writer.writeAll("for (;"); try f.writeCValue(writer, it, .Other); try writer.print("<{d};++", .{vector_len}); try f.writeCValue(writer, it, .Other); @@ -5647,44 +6286,57 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); + try freeLocal(f, inst, it.local, 0); + return accum; } fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - - const inst_ty = f.air.typeOfIndex(inst); const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const inst_ty = f.air.typeOfIndex(inst); const len = @intCast(usize, inst_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); + const gpa = f.object.dg.gpa; + const resolved_elements = try gpa.alloc(CValue, elements.len); + defer gpa.free(resolved_elements); + for (elements) |element, i| { + resolved_elements[i] = try f.resolveInst(element); + } + { + var bt = iterateBigTomb(f, inst); + for (elements) |element| { + try bt.feed(element); + } + } + + if (f.liveness.isUnused(inst)) return CValue.none; + const target = f.object.dg.module.getTarget(); - const mutability: Mutability = for (elements) |element| { - if (lowersToArray(f.air.typeOf(element), target)) break .Mut; - } else .Const; const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, mutability); - try writer.writeAll(" = "); + const local = try f.allocLocal(inst, inst_ty); switch (inst_ty.zigTypeTag()) { .Array, .Vector => { const elem_ty = inst_ty.childType(); - try writer.writeByte('{'); - var empty = true; - for (elements) |element| { - if (!empty) try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(element), .Initializer); - empty = false; + for (resolved_elements) |element, i| { + try f.writeCValue(writer, local, .Other); + try writer.print("[{d}] = ", .{i}); + try f.writeCValue(writer, element, .Other); + try writer.writeAll(";\n"); } if (inst_ty.sentinel()) |sentinel| { - if (!empty) try writer.writeAll(", "); - try f.object.dg.renderValue(writer, elem_ty, sentinel, .Initializer); - empty = false; + try f.writeCValue(writer, local, .Other); + try writer.print("[{d}] = ", .{resolved_elements.len}); + try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other); + try writer.writeAll(";\n"); } - if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); - try writer.writeAll("};\n"); }, .Struct => switch (inst_ty.containerLayout()) { .Auto, .Extern => { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll(")"); try writer.writeByte('{'); var empty = true; for (elements) |element, index| { @@ -5698,7 +6350,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const element_ty = f.air.typeOf(element); try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { .Array => CValue{ .undef = element_ty }, - else => try f.resolveInst(element), + else => resolved_elements[index], }, .Initializer); empty = false; } @@ -5721,7 +6373,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("memcpy("); try f.writeCValueMember(writer, local, field_name); try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument); + try f.writeCValue(writer, resolved_elements[index], .FunctionArgument); try writer.writeAll(", sizeof("); try f.renderTypecast(writer, element_ty); try writer.writeAll("));\n"); @@ -5730,6 +6382,10 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } }, .Packed => { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll(")"); const int_info = inst_ty.intInfo(target); var bit_offset_ty_pl = Type.Payload.Bits{ @@ -5754,7 +6410,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { empty = false; } empty = true; - for (elements) |element, index| { + for (resolved_elements) |element, index| { const field_ty = inst_ty.structFieldType(index); if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -5772,7 +6428,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { }); try writer.writeByte(')'); } - try f.writeCValue(writer, try f.resolveInst(element), .Other); + try f.writeCValue(writer, element, .Other); try writer.writeAll(", "); try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); @@ -5793,26 +6449,31 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - 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 CValue.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; const field_name = union_obj.fields.keys()[extra.field_index]; const payload = try f.resolveInst(extra.init); + try reap(f, inst, &.{extra.init}); const writer = f.object.writer(); - const local = try f.allocLocal(union_ty, .Const); + const local = try f.allocLocal(inst, union_ty); if (union_obj.layout == .Packed) { + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, payload, .Initializer); try writer.writeAll(";\n"); return local; } - try writer.writeAll(" = {"); if (union_ty.unionTagTypeSafety()) |tag_ty| { const layout = union_ty.unionGetLayout(target); if (layout.tag_size != 0) { @@ -5827,16 +6488,20 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { var int_pl: Value.Payload.U64 = undefined; const int_val = tag_val.enumToInt(tag_ty, &int_pl); - try writer.print(".tag = {}, ", .{try f.fmtIntLiteral(tag_ty, int_val)}); + try f.writeCValue(writer, local, .Other); + try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)}); } - try writer.writeAll(".payload = {"); + try f.writeCValue(writer, local, .Other); + try writer.print(".payload.{ } = ", .{fmtIdent(field_name)}); + try f.writeCValue(writer, payload, .Other); + try writer.writeAll(";\n"); + return local; } + try f.writeCValue(writer, local, .Other); try writer.print(".{ } = ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, payload, .Initializer); - - if (union_ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); - try writer.writeAll("};\n"); + try f.writeCValue(writer, payload, .Other); + try writer.writeAll(";\n"); return local; } @@ -5851,6 +6516,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { .instruction => return CValue.none, } const ptr = try f.resolveInst(prefetch.ptr); + try reap(f, inst, &.{prefetch.ptr}); const writer = f.object.writer(); try writer.writeAll("zig_prefetch("); try f.writeCValue(writer, ptr, .FunctionArgument); @@ -5867,7 +6533,8 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload}); @@ -5881,7 +6548,9 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(pl_op.operand); - const local = try f.allocLocal(inst_ty, .Const); + try reap(f, inst, &.{pl_op.operand}); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload}); @@ -5891,15 +6560,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { } fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - const inst_ty = f.air.typeOfIndex(inst); const un_op = f.air.instructions.items(.data)[inst].un_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const operand_ty = f.air.typeOf(un_op); - const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_neg_"); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); @@ -5909,12 +6583,17 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { } fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; const un_op = f.air.instructions.items(.data)[inst].un_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{un_op}); + return CValue.none; + } + const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); - const operand = try f.resolveInst(un_op); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_libc_name_"); try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); @@ -5925,18 +6604,21 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal return local; } -fn airBinFloatOp( - f: *Function, - inst: Air.Inst.Index, - operation: []const u8, -) !void { +fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const writer = f.object.writer(); - const inst_ty = f.air.typeOfIndex(inst); + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return CValue.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 }); - try writer.writeAll("zig_libc_name_"); + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = zig_libc_name_"); try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); try writer.writeAll(operation); @@ -5944,19 +6626,25 @@ fn airBinFloatOp( try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); - try writer.writeAll(")"); + try writer.writeAll(");\n"); + return local; } fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; - const extra = f.air.extraData(Air.Bin, pl_op.payload).data; + 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 CValue.none; + } const inst_ty = f.air.typeOfIndex(inst); - const mulend1 = try f.resolveInst(extra.lhs); - const mulend2 = try f.resolveInst(extra.rhs); + const mulend1 = try f.resolveInst(bin_op.lhs); + const mulend2 = try f.resolveInst(bin_op.rhs); const addend = try f.resolveInst(pl_op.operand); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_libc_name_"); try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeAll("(fma)("); @@ -6335,3 +7023,105 @@ fn loweredArrayInfo(ty: Type, target: std.Target) ?Type.ArrayInfo { }, } } + +fn reap(f: *Function, inst: Air.Inst.Index, operands: []const Air.Inst.Ref) !void { + assert(operands.len <= Liveness.bpi - 1); + var tomb_bits = f.liveness.getTombBits(inst); + for (operands) |operand| { + const dies = @truncate(u1, tomb_bits) != 0; + tomb_bits >>= 1; + if (!dies) continue; + try die(f, inst, operand); + } +} + +fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { + const ref_inst = Air.refToIndex(ref) orelse return; + if (f.air.instructions.items(.tag)[ref_inst] == .constant) return; + const c_value = (f.value_map.fetchRemove(ref) orelse return).value; + const local_index = switch (c_value) { + .local => |l| l, + else => return, + }; + try freeLocal(f, inst, local_index, ref_inst); +} + +fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_inst: Air.Inst.Index) !void { + 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 (local.loop_depth < f.free_locals_clone_depth) return; + const gop = try f.free_locals_stack.items[local.loop_depth].getOrPutContext( + gpa, + local.ty, + f.tyHashCtx(), + ); + if (!gop.found_existing) gop.value_ptr.* = .{}; + if (std.debug.runtime_safety) { + // If this trips, it means a local is being inserted into the + // free_locals map while it already exists in the map, which is not + // allowed. + assert(mem.indexOfScalar(LocalIndex, gop.value_ptr.items, local_index) == null); + // If this trips, an unfreeable allocation was attempted to be freed. + assert(!f.allocs.contains(local_index)); + } + try gop.value_ptr.append(gpa, local_index); +} + +const BigTomb = struct { + f: *Function, + inst: Air.Inst.Index, + lbt: Liveness.BigTomb, + + fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) !void { + const dies = bt.lbt.feed(); + if (!dies) return; + try die(bt.f, bt.inst, op_ref); + } +}; + +fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb { + return .{ + .f = f, + .inst = inst, + .lbt = f.liveness.iterateBigTomb(inst), + }; +} + +/// A naive clone of this map would create copies of the ArrayList which is +/// stored in the values. This function additionally clones the values. +fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { + var cloned = try map.clone(gpa); + const values = cloned.values(); + var i: usize = 0; + errdefer { + cloned.deinit(gpa); + while (i > 0) { + i -= 1; + values[i].deinit(gpa); + } + } + while (i < values.len) : (i += 1) { + values[i] = try values[i].clone(gpa); + } + return cloned; +} + +fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { + for (map.values()) |*value| { + value.deinit(gpa); + } + map.deinit(gpa); +} + +fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void { + for (f.locals.items[pre_locals_len..]) |*local, local_offset| { + const local_index = pre_locals_len + @intCast(LocalIndex, local_offset); + if (f.allocs.contains(local_index)) continue; // allocs are not freeable + + // free more deeply nested locals from other branches at current depth + assert(local.loop_depth >= f.free_locals_stack.items.len - 1); + local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); + try freeLocal(f, inst, local_index, 0); + } +} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3fd1effc21..29d074ea1c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5346,7 +5346,8 @@ pub const FuncGen = struct { const err_union_ty = self.air.typeOf(pl_op.operand); const payload_ty = self.air.typeOfIndex(inst); const can_elide_load = if (isByRef(payload_ty)) self.canElideLoad(body_tail) else false; - return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, payload_ty); + const is_unused = self.liveness.isUnused(inst); + return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused, payload_ty); } fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { @@ -5356,7 +5357,8 @@ pub const FuncGen = struct { const body = self.air.extra[extra.end..][0..extra.data.body_len]; const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); const payload_ty = self.air.typeOfIndex(inst); - return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, payload_ty); + const is_unused = self.liveness.isUnused(inst); + return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, payload_ty); } fn lowerTry( @@ -5366,6 +5368,7 @@ pub const FuncGen = struct { err_union_ty: Type, operand_is_ptr: bool, can_elide_load: bool, + is_unused: bool, result_ty: Type, ) !?*llvm.Value { const payload_ty = err_union_ty.errorUnionPayload(); @@ -5405,6 +5408,9 @@ pub const FuncGen = struct { fg.builder.positionBuilderAtEnd(continue_block); } + if (is_unused) { + return null; + } if (!payload_has_bits) { if (!operand_is_ptr) return null; diff --git a/src/link/C.zig b/src/link/C.zig index a2d7ee143c..ad50a20b99 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -133,19 +133,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes .code = code.toManaged(module.gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }, + .arena = std.heap.ArenaAllocator.init(module.gpa), }; function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; - defer { - function.blocks.deinit(module.gpa); - function.value_map.deinit(); - function.object.code.deinit(); - for (function.object.dg.typedefs.values()) |typedef| { - module.gpa.free(typedef.rendered); - } - function.object.dg.typedefs.deinit(); - function.object.dg.fwd_decl.deinit(); - } + defer function.deinit(module.gpa); codegen.genFunc(&function) catch |err| switch (err) { error.AnalysisFail => { |
