aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-12-04 21:59:01 -0500
committerGitHub <noreply@github.com>2022-12-04 21:59:01 -0500
commit70ad5bc3631c330eb41b9379d7e93f0647da2fd6 (patch)
tree8b9e7c78245891efee3cc8b4f0e3ad770704bb7e /src
parentbdb6fb57639a4508dda523301c5eaf31e8d89edf (diff)
parent7d3cc3bc8d772519d390b7f13346eeab73bc0c21 (diff)
downloadzig-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.zig14
-rw-r--r--src/codegen/c.zig2418
-rw-r--r--src/codegen/llvm.zig10
-rw-r--r--src/link/C.zig12
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 => {