Merge pull request #15355 from mlugg/feat/liveness-control-flow

Liveness: control flow analysis and other goodies
This commit is contained in:
Andrew Kelley 2023-04-21 13:32:25 -07:00 committed by GitHub
commit 528b66f6ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
177 changed files with 3901 additions and 4775 deletions

View File

@ -13,7 +13,7 @@ var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
pub fn main() void {
if (builtin.zig_backend == .stage2_wasm or
builtin.zig_backend == .stage2_x86_64 or
(builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) or
builtin.zig_backend == .stage2_aarch64)
{
return mainSimple() catch @panic("test failure");

View File

@ -1375,3 +1375,217 @@ pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
}
return bytes[0..end :0];
}
/// Returns whether the given instruction must always be lowered, for instance because it can cause
/// side effects. If an instruction does not need to be lowered, and Liveness determines its result
/// is unused, backends should avoid lowering it.
pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {
const data = air.instructions.items(.data)[inst];
return switch (air.instructions.items(.tag)[inst]) {
.arg,
.block,
.loop,
.br,
.trap,
.breakpoint,
.call,
.call_always_tail,
.call_never_tail,
.call_never_inline,
.cond_br,
.switch_br,
.@"try",
.try_ptr,
.dbg_stmt,
.dbg_block_begin,
.dbg_block_end,
.dbg_inline_begin,
.dbg_inline_end,
.dbg_var_ptr,
.dbg_var_val,
.ret,
.ret_load,
.store,
.unreach,
.optional_payload_ptr_set,
.errunion_payload_ptr_set,
.set_union_tag,
.memset,
.memcpy,
.cmpxchg_weak,
.cmpxchg_strong,
.fence,
.atomic_store_unordered,
.atomic_store_monotonic,
.atomic_store_release,
.atomic_store_seq_cst,
.atomic_rmw,
.prefetch,
.wasm_memory_grow,
.set_err_return_trace,
.vector_store_elem,
.c_va_arg,
.c_va_copy,
.c_va_end,
.c_va_start,
=> true,
.add,
.add_optimized,
.addwrap,
.addwrap_optimized,
.add_sat,
.sub,
.sub_optimized,
.subwrap,
.subwrap_optimized,
.sub_sat,
.mul,
.mul_optimized,
.mulwrap,
.mulwrap_optimized,
.mul_sat,
.div_float,
.div_float_optimized,
.div_trunc,
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
.div_exact,
.div_exact_optimized,
.rem,
.rem_optimized,
.mod,
.mod_optimized,
.ptr_add,
.ptr_sub,
.max,
.min,
.add_with_overflow,
.sub_with_overflow,
.mul_with_overflow,
.shl_with_overflow,
.alloc,
.ret_ptr,
.bit_and,
.bit_or,
.shr,
.shr_exact,
.shl,
.shl_exact,
.shl_sat,
.xor,
.not,
.bitcast,
.ret_addr,
.frame_addr,
.clz,
.ctz,
.popcount,
.byte_swap,
.bit_reverse,
.sqrt,
.sin,
.cos,
.tan,
.exp,
.exp2,
.log,
.log2,
.log10,
.fabs,
.floor,
.ceil,
.round,
.trunc_float,
.neg,
.neg_optimized,
.cmp_lt,
.cmp_lt_optimized,
.cmp_lte,
.cmp_lte_optimized,
.cmp_eq,
.cmp_eq_optimized,
.cmp_gte,
.cmp_gte_optimized,
.cmp_gt,
.cmp_gt_optimized,
.cmp_neq,
.cmp_neq_optimized,
.cmp_vector,
.cmp_vector_optimized,
.constant,
.const_ty,
.is_null,
.is_non_null,
.is_null_ptr,
.is_non_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.bool_and,
.bool_or,
.ptrtoint,
.bool_to_int,
.fptrunc,
.fpext,
.intcast,
.trunc,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
.wrap_errunion_payload,
.wrap_errunion_err,
.struct_field_ptr,
.struct_field_ptr_index_0,
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
.struct_field_ptr_index_3,
.struct_field_val,
.get_union_tag,
.slice,
.slice_len,
.slice_ptr,
.ptr_slice_len_ptr,
.ptr_slice_ptr_ptr,
.array_elem_val,
.slice_elem_ptr,
.ptr_elem_ptr,
.array_to_slice,
.float_to_int,
.float_to_int_optimized,
.int_to_float,
.reduce,
.reduce_optimized,
.splat,
.shuffle,
.select,
.is_named_enum_value,
.tag_name,
.error_name,
.error_set_has_value,
.aggregate_init,
.union_init,
.mul_add,
.field_parent_ptr,
.wasm_memory_size,
.cmp_lt_errors_len,
.err_return_trace,
.addrspace_cast,
.save_err_return_trace_index,
.work_item_id,
.work_group_size,
.work_group_id,
=> false,
.assembly => @truncate(u1, air.extraData(Air.Asm, data.ty_pl.payload).data.flags >> 31) != 0,
.load => air.typeOf(data.ty_op.operand).isVolatilePtr(),
.slice_elem_val, .ptr_elem_val => air.typeOf(data.bin_op.lhs).isVolatilePtr(),
.atomic_load => air.typeOf(data.atomic_load.ptr).isVolatilePtr(),
};
}

View File

@ -3095,6 +3095,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
.file_failure,
.sema_failure,
.liveness_failure,
.codegen_failure,
.dependency_failure,
.sema_failure_retryable,
@ -3145,7 +3146,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
// emit-h only requires semantic analysis of the Decl to be complete,
// it does not depend on machine code generation to succeed.
.codegen_failure, .codegen_failure_retryable, .complete => {
.liveness_failure, .codegen_failure, .codegen_failure_retryable, .complete => {
const named_frame = tracy.namedFrame("emit_h_decl");
defer named_frame.end();

File diff suppressed because it is too large Load Diff

610
src/Liveness/Verify.zig Normal file
View File

@ -0,0 +1,610 @@
//! Verifies that liveness information is valid.
gpa: std.mem.Allocator,
air: Air,
liveness: Liveness,
live: LiveMap = .{},
blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, LiveMap) = .{},
pub const Error = error{ LivenessInvalid, OutOfMemory };
pub fn deinit(self: *Verify) void {
self.live.deinit(self.gpa);
var block_it = self.blocks.valueIterator();
while (block_it.next()) |block| block.deinit(self.gpa);
self.blocks.deinit(self.gpa);
self.* = undefined;
}
pub fn verify(self: *Verify) Error!void {
self.live.clearRetainingCapacity();
self.blocks.clearRetainingCapacity();
try self.verifyBody(self.air.getMainBody());
// We don't care about `self.live` now, because the loop body was noreturn - everything being dead was checked on `ret` etc
assert(self.blocks.count() == 0);
}
const LiveMap = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
const tag = self.air.instructions.items(.tag);
const data = self.air.instructions.items(.data);
for (body) |inst| {
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
// This instruction will not be lowered and should be ignored.
continue;
}
switch (tag[inst]) {
// no operands
.arg,
.alloc,
.ret_ptr,
.constant,
.const_ty,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
.dbg_inline_end,
.dbg_block_begin,
.dbg_block_end,
.fence,
.ret_addr,
.frame_addr,
.wasm_memory_size,
.err_return_trace,
.save_err_return_trace_index,
.c_va_start,
.work_item_id,
.work_group_size,
.work_group_id,
=> try self.verifyInst(inst, .{ .none, .none, .none }),
.trap, .unreach => {
try self.verifyInst(inst, .{ .none, .none, .none });
// This instruction terminates the function, so everything should be dead
if (self.live.count() > 0) return invalid("%{}: instructions still alive", .{inst});
},
// unary
.not,
.bitcast,
.load,
.fpext,
.fptrunc,
.intcast,
.trunc,
.optional_payload,
.optional_payload_ptr,
.optional_payload_ptr_set,
.errunion_payload_ptr_set,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
.wrap_errunion_payload,
.wrap_errunion_err,
.slice_ptr,
.slice_len,
.ptr_slice_len_ptr,
.ptr_slice_ptr_ptr,
.struct_field_ptr_index_0,
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
.struct_field_ptr_index_3,
.array_to_slice,
.float_to_int,
.float_to_int_optimized,
.int_to_float,
.get_union_tag,
.clz,
.ctz,
.popcount,
.byte_swap,
.bit_reverse,
.splat,
.error_set_has_value,
.addrspace_cast,
.c_va_arg,
.c_va_copy,
=> {
const ty_op = data[inst].ty_op;
try self.verifyInst(inst, .{ ty_op.operand, .none, .none });
},
.is_null,
.is_non_null,
.is_null_ptr,
.is_non_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.ptrtoint,
.bool_to_int,
.is_named_enum_value,
.tag_name,
.error_name,
.sqrt,
.sin,
.cos,
.tan,
.exp,
.exp2,
.log,
.log2,
.log10,
.fabs,
.floor,
.ceil,
.round,
.trunc_float,
.neg,
.neg_optimized,
.cmp_lt_errors_len,
.set_err_return_trace,
.c_va_end,
=> {
const un_op = data[inst].un_op;
try self.verifyInst(inst, .{ un_op, .none, .none });
},
.ret,
.ret_load,
=> {
const un_op = data[inst].un_op;
try self.verifyInst(inst, .{ un_op, .none, .none });
// This instruction terminates the function, so everything should be dead
if (self.live.count() > 0) return invalid("%{}: instructions still alive", .{inst});
},
.dbg_var_ptr,
.dbg_var_val,
.wasm_memory_grow,
=> {
const pl_op = data[inst].pl_op;
try self.verifyInst(inst, .{ pl_op.operand, .none, .none });
},
.prefetch => {
const prefetch = data[inst].prefetch;
try self.verifyInst(inst, .{ prefetch.ptr, .none, .none });
},
.reduce,
.reduce_optimized,
=> {
const reduce = data[inst].reduce;
try self.verifyInst(inst, .{ reduce.operand, .none, .none });
},
.union_init => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.init, .none, .none });
},
.struct_field_ptr, .struct_field_val => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.struct_operand, .none, .none });
},
.field_parent_ptr => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.field_ptr, .none, .none });
},
.atomic_load => {
const atomic_load = data[inst].atomic_load;
try self.verifyInst(inst, .{ atomic_load.ptr, .none, .none });
},
// binary
.add,
.add_optimized,
.addwrap,
.addwrap_optimized,
.add_sat,
.sub,
.sub_optimized,
.subwrap,
.subwrap_optimized,
.sub_sat,
.mul,
.mul_optimized,
.mulwrap,
.mulwrap_optimized,
.mul_sat,
.div_float,
.div_float_optimized,
.div_trunc,
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
.div_exact,
.div_exact_optimized,
.rem,
.rem_optimized,
.mod,
.mod_optimized,
.bit_and,
.bit_or,
.xor,
.cmp_lt,
.cmp_lt_optimized,
.cmp_lte,
.cmp_lte_optimized,
.cmp_eq,
.cmp_eq_optimized,
.cmp_gte,
.cmp_gte_optimized,
.cmp_gt,
.cmp_gt_optimized,
.cmp_neq,
.cmp_neq_optimized,
.bool_and,
.bool_or,
.store,
.array_elem_val,
.slice_elem_val,
.ptr_elem_val,
.shl,
.shl_exact,
.shl_sat,
.shr,
.shr_exact,
.atomic_store_unordered,
.atomic_store_monotonic,
.atomic_store_release,
.atomic_store_seq_cst,
.set_union_tag,
.min,
.max,
=> {
const bin_op = data[inst].bin_op;
try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none });
},
.add_with_overflow,
.sub_with_overflow,
.mul_with_overflow,
.shl_with_overflow,
.ptr_add,
.ptr_sub,
.ptr_elem_ptr,
.slice_elem_ptr,
.slice,
=> {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.lhs, extra.rhs, .none });
},
.shuffle => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.a, extra.b, .none });
},
.cmp_vector,
.cmp_vector_optimized,
=> {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.lhs, extra.rhs, .none });
},
.atomic_rmw => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data;
try self.verifyInst(inst, .{ pl_op.operand, extra.operand, .none });
},
// ternary
.select => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
},
.mul_add => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
try self.verifyInst(inst, .{ extra.lhs, extra.rhs, pl_op.operand });
},
.vector_store_elem => {
const vector_store_elem = data[inst].vector_store_elem;
const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data;
try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs });
},
.memset,
.memcpy,
=> {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs });
},
.cmpxchg_strong,
.cmpxchg_weak,
=> {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
try self.verifyInst(inst, .{ extra.ptr, extra.expected_value, extra.new_value });
},
// big tombs
.aggregate_init => {
const ty_pl = data[inst].ty_pl;
const aggregate_ty = self.air.getRefType(ty_pl.ty);
const len = @intCast(usize, aggregate_ty.arrayLen());
const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
var bt = self.liveness.iterateBigTomb(inst);
for (elements) |element| {
try self.verifyOperand(inst, element, bt.feed());
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
.call, .call_always_tail, .call_never_tail, .call_never_inline => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.Call, pl_op.payload);
const args = @ptrCast(
[]const Air.Inst.Ref,
self.air.extra[extra.end..][0..extra.data.args_len],
);
var bt = self.liveness.iterateBigTomb(inst);
try self.verifyOperand(inst, pl_op.operand, bt.feed());
for (args) |arg| {
try self.verifyOperand(inst, arg, bt.feed());
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
.assembly => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
var extra_i = extra.end;
const outputs = @ptrCast(
[]const Air.Inst.Ref,
self.air.extra[extra_i..][0..extra.data.outputs_len],
);
extra_i += outputs.len;
const inputs = @ptrCast(
[]const Air.Inst.Ref,
self.air.extra[extra_i..][0..extra.data.inputs_len],
);
extra_i += inputs.len;
var bt = self.liveness.iterateBigTomb(inst);
for (outputs) |output| {
if (output != .none) {
try self.verifyOperand(inst, output, bt.feed());
}
}
for (inputs) |input| {
try self.verifyOperand(inst, input, bt.feed());
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
// control flow
.@"try" => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.Try, pl_op.payload);
const try_body = self.air.extra[extra.end..][0..extra.data.body_len];
const cond_br_liveness = self.liveness.getCondBr(inst);
try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
var live = try self.live.clone(self.gpa);
defer live.deinit(self.gpa);
for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyBody(try_body);
self.live.deinit(self.gpa);
self.live = live.move();
for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyInst(inst, .{ .none, .none, .none });
},
.try_ptr => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
const try_body = self.air.extra[extra.end..][0..extra.data.body_len];
const cond_br_liveness = self.liveness.getCondBr(inst);
try self.verifyOperand(inst, extra.data.ptr, self.liveness.operandDies(inst, 0));
var live = try self.live.clone(self.gpa);
defer live.deinit(self.gpa);
for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyBody(try_body);
self.live.deinit(self.gpa);
self.live = live.move();
for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyInst(inst, .{ .none, .none, .none });
},
.br => {
const br = data[inst].br;
const gop = try self.blocks.getOrPut(self.gpa, br.block_inst);
try self.verifyOperand(inst, br.operand, self.liveness.operandDies(inst, 0));
if (gop.found_existing) {
try self.verifyMatchingLiveness(br.block_inst, gop.value_ptr.*);
} else {
gop.value_ptr.* = try self.live.clone(self.gpa);
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
.block => {
const ty_pl = data[inst].ty_pl;
const block_ty = self.air.getRefType(ty_pl.ty);
const extra = self.air.extraData(Air.Block, ty_pl.payload);
const block_body = self.air.extra[extra.end..][0..extra.data.body_len];
const block_liveness = self.liveness.getBlock(inst);
var orig_live = try self.live.clone(self.gpa);
defer orig_live.deinit(self.gpa);
assert(!self.blocks.contains(inst));
try self.verifyBody(block_body);
// Liveness data after the block body is garbage, but we want to
// restore it to verify deaths
self.live.deinit(self.gpa);
self.live = orig_live.move();
for (block_liveness.deaths) |death| try self.verifyDeath(inst, death);
if (block_ty.isNoReturn()) {
assert(!self.blocks.contains(inst));
} else {
var live = self.blocks.fetchRemove(inst).?.value;
defer live.deinit(self.gpa);
try self.verifyMatchingLiveness(inst, live);
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
.loop => {
const ty_pl = data[inst].ty_pl;
const extra = self.air.extraData(Air.Block, ty_pl.payload);
const loop_body = self.air.extra[extra.end..][0..extra.data.body_len];
var live = try self.live.clone(self.gpa);
defer live.deinit(self.gpa);
try self.verifyBody(loop_body);
// The same stuff should be alive after the loop as before it
try self.verifyMatchingLiveness(inst, live);
try self.verifyInst(inst, .{ .none, .none, .none });
},
.cond_br => {
const pl_op = data[inst].pl_op;
const extra = self.air.extraData(Air.CondBr, pl_op.payload);
const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
const cond_br_liveness = self.liveness.getCondBr(inst);
try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
var live = try self.live.clone(self.gpa);
defer live.deinit(self.gpa);
for (cond_br_liveness.then_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyBody(then_body);
self.live.deinit(self.gpa);
self.live = live.move();
for (cond_br_liveness.else_deaths) |death| try self.verifyDeath(inst, death);
try self.verifyBody(else_body);
try self.verifyInst(inst, .{ .none, .none, .none });
},
.switch_br => {
const pl_op = data[inst].pl_op;
const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
var extra_index = switch_br.end;
var case_i: u32 = 0;
const switch_br_liveness = try self.liveness.getSwitchBr(
self.gpa,
inst,
switch_br.data.cases_len + 1,
);
defer self.gpa.free(switch_br_liveness.deaths);
try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
var live = self.live.move();
defer live.deinit(self.gpa);
while (case_i < switch_br.data.cases_len) : (case_i += 1) {
const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
const items = @ptrCast(
[]const Air.Inst.Ref,
self.air.extra[case.end..][0..case.data.items_len],
);
const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
extra_index = case.end + items.len + case_body.len;
self.live.deinit(self.gpa);
self.live = try live.clone(self.gpa);
for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
try self.verifyBody(case_body);
}
const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
if (else_body.len > 0) {
self.live.deinit(self.gpa);
self.live = try live.clone(self.gpa);
for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
try self.verifyBody(else_body);
}
try self.verifyInst(inst, .{ .none, .none, .none });
},
}
}
}
fn verifyDeath(self: *Verify, inst: Air.Inst.Index, operand: Air.Inst.Index) Error!void {
try self.verifyOperand(inst, Air.indexToRef(operand), true);
}
fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies: bool) Error!void {
const operand = Air.refToIndex(op_ref) orelse return;
switch (self.air.instructions.items(.tag)[operand]) {
.constant, .const_ty => {},
else => {
if (dies) {
if (!self.live.remove(operand)) return invalid("%{}: dead operand %{} reused and killed again", .{ inst, operand });
} else {
if (!self.live.contains(operand)) return invalid("%{}: dead operand %{} reused", .{ inst, operand });
}
},
}
}
fn verifyInst(
self: *Verify,
inst: Air.Inst.Index,
operands: [Liveness.bpi - 1]Air.Inst.Ref,
) Error!void {
for (operands, 0..) |operand, operand_index| {
const dies = self.liveness.operandDies(inst, @intCast(Liveness.OperandInt, operand_index));
try self.verifyOperand(inst, operand, dies);
}
const tag = self.air.instructions.items(.tag);
switch (tag[inst]) {
.constant, .const_ty => unreachable,
else => {
if (self.liveness.isUnused(inst)) {
assert(!self.live.contains(inst));
} else {
try self.live.putNoClobber(self.gpa, inst, {});
}
},
}
}
fn verifyMatchingLiveness(self: *Verify, block: Air.Inst.Index, live: LiveMap) Error!void {
if (self.live.count() != live.count()) return invalid("%{}: different deaths across branches", .{block});
var live_it = self.live.keyIterator();
while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("%{}: different deaths across branches", .{block});
}
fn invalid(comptime fmt: []const u8, args: anytype) error{LivenessInvalid} {
log.err(fmt, args);
return error.LivenessInvalid;
}
const std = @import("std");
const assert = std.debug.assert;
const log = std.log.scoped(.liveness_verify);
const Air = @import("../Air.zig");
const Liveness = @import("../Liveness.zig");
const Verify = @This();

View File

@ -483,6 +483,8 @@ pub const Decl = struct {
/// and attempting semantic analysis again may succeed.
sema_failure_retryable,
/// There will be a corresponding ErrorMsg in Module.failed_decls.
liveness_failure,
/// There will be a corresponding ErrorMsg in Module.failed_decls.
codegen_failure,
/// There will be a corresponding ErrorMsg in Module.failed_decls.
/// This indicates the failure was something like running out of disk space,
@ -4129,6 +4131,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
.file_failure,
.sema_failure,
.sema_failure_retryable,
.liveness_failure,
.codegen_failure,
.dependency_failure,
.codegen_failure_retryable,
@ -4222,6 +4225,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
.dependency_failure,
.sema_failure,
.sema_failure_retryable,
.liveness_failure,
.codegen_failure,
.codegen_failure_retryable,
.complete,
@ -4247,6 +4251,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
.file_failure,
.sema_failure,
.liveness_failure,
.codegen_failure,
.dependency_failure,
.sema_failure_retryable,
@ -4306,6 +4311,33 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
std.debug.print("# End Function AIR: {s}\n\n", .{fqn});
}
if (std.debug.runtime_safety) {
var verify = Liveness.Verify{
.gpa = gpa,
.air = air,
.liveness = liveness,
};
defer verify.deinit();
verify.verify() catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
mod.failed_decls.putAssumeCapacityNoClobber(
decl_index,
try Module.ErrorMsg.create(
gpa,
decl.srcLoc(),
"invalid liveness: {s}",
.{@errorName(err)},
),
);
decl.analysis = .liveness_failure;
return error.AnalysisFail;
},
};
}
if (no_bin_file and !dump_llvm_ir) return;
comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) {
@ -4349,6 +4381,7 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
.dependency_failure,
.sema_failure,
.sema_failure_retryable,
.liveness_failure,
.codegen_failure,
.codegen_failure_retryable,
.complete,

View File

@ -655,6 +655,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const air_tags = self.air.instructions.items(.tag);
for (body) |inst| {
// TODO: remove now-redundant isUnused calls from AIR handler functions
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
continue;
}
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
@ -5000,17 +5005,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const loop = self.air.extraData(Air.Block, ty_pl.payload);
const body = self.air.extra[loop.end..][0..loop.data.body_len];
const liveness_loop = self.liveness.getLoop(inst);
const start_index = @intCast(u32, self.mir_instructions.len);
try self.genBody(body);
try self.jump(start_index);
try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
for (liveness_loop.deaths) |operand| {
self.processDeath(operand);
}
return self.finishAirBookkeeping();
}

View File

@ -639,6 +639,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const air_tags = self.air.instructions.items(.tag);
for (body) |inst| {
// TODO: remove now-redundant isUnused calls from AIR handler functions
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
continue;
}
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
@ -4923,17 +4928,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const loop = self.air.extraData(Air.Block, ty_pl.payload);
const body = self.air.extra[loop.end..][0..loop.data.body_len];
const liveness_loop = self.liveness.getLoop(inst);
const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
try self.genBody(body);
try self.jump(start_index);
try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
for (liveness_loop.deaths) |operand| {
self.processDeath(operand);
}
return self.finishAirBookkeeping();
}

View File

@ -473,6 +473,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const air_tags = self.air.instructions.items(.tag);
for (body) |inst| {
// TODO: remove now-redundant isUnused calls from AIR handler functions
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
continue;
}
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);

View File

@ -489,6 +489,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const air_tags = self.air.instructions.items(.tag);
for (body) |inst| {
// TODO: remove now-redundant isUnused calls from AIR handler functions
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
continue;
}
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
@ -1750,17 +1755,11 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const loop = self.air.extraData(Air.Block, ty_pl.payload);
const body = self.air.extra[loop.end .. loop.end + loop.data.body_len];
const liveness_loop = self.liveness.getLoop(inst);
const start = @intCast(u32, self.mir_instructions.len);
try self.genBody(body);
try self.jump(start);
try self.ensureProcessDeathCapacity(liveness_loop.deaths.len);
for (liveness_loop.deaths) |operand| {
self.processDeath(operand);
}
return self.finishAirBookkeeping();
}

View File

@ -2009,9 +2009,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
for (body) |inst| {
if (func.liveness.isUnused(inst) and !func.air.mustLower(inst)) {
continue;
}
const old_bookkeeping_value = func.air_bookkeeping;
// TODO: Determine why we need to pre-allocate an extra 4 possible values here.
try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi + 4);
try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi);
try func.genInst(inst);
if (builtin.mode == .Debug and func.air_bookkeeping < old_bookkeeping_value + 1) {
@ -2185,7 +2187,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
}
const result_value = result_value: {
if (func.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
break :result_value WValue{ .none = {} };
} else if (ret_ty.isNoReturn()) {
try func.addTag(.@"unreachable");
@ -2494,7 +2496,6 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const lhs = try func.resolveInst(bin_op.lhs);
const rhs = try func.resolveInst(bin_op.rhs);
const ty = func.air.typeOf(bin_op.lhs);
@ -2649,7 +2650,6 @@ const FloatOp = enum {
fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
const ty = func.air.typeOf(un_op);
@ -2723,7 +2723,6 @@ fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) In
fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const lhs = try func.resolveInst(bin_op.lhs);
const rhs = try func.resolveInst(bin_op.rhs);
@ -3183,7 +3182,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const loop = func.air.extraData(Air.Block, ty_pl.payload);
const body = func.air.extra[loop.end..][0..loop.data.body_len];
const liveness_loop = func.liveness.getLoop(inst);
// result type of loop is always 'noreturn', meaning we can always
// emit the wasm type 'block_empty'.
@ -3194,11 +3192,6 @@ fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
try func.addLabel(.br, 0);
try func.endBlock();
try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_loop.deaths.len));
for (liveness_loop.deaths) |death| {
func.processDeath(Air.indexToRef(death));
}
func.finishAir(inst, .none, &.{});
}
@ -3224,9 +3217,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
func.branches.appendAssumeCapacity(.{});
try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
for (liveness_condbr.else_deaths) |death| {
func.processDeath(Air.indexToRef(death));
}
try func.genBody(else_body);
try func.endBlock();
var else_stack = func.branches.pop();
@ -3235,9 +3225,6 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
// Outer block that matches the condition
func.branches.appendAssumeCapacity(.{});
try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
for (liveness_condbr.then_deaths) |death| {
func.processDeath(Air.indexToRef(death));
}
try func.genBody(then_body);
var then_stack = func.branches.pop();
defer then_stack.deinit(func.gpa);
@ -3255,7 +3242,7 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
const target_keys = target_slice.items(.key);
const target_values = target_slice.items(.value);
try parent.values.ensureUnusedCapacity(func.gpa, branch.values.count());
try parent.values.ensureTotalCapacity(func.gpa, parent.values.capacity() + branch.values.count());
for (target_keys, 0..) |key, index| {
// TODO: process deaths from branches
parent.values.putAssumeCapacity(key, target_values[index]);
@ -3264,7 +3251,6 @@ fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const lhs = try func.resolveInst(bin_op.lhs);
const rhs = try func.resolveInst(bin_op.rhs);
@ -3381,7 +3367,6 @@ fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const operand_ty = func.air.typeOf(ty_op.operand);
@ -3447,7 +3432,7 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
const result = if (!func.liveness.isUnused(inst)) result: {
const result = result: {
const operand = try func.resolveInst(ty_op.operand);
const wanted_ty = func.air.typeOfIndex(inst);
const given_ty = func.air.typeOf(ty_op.operand);
@ -3456,7 +3441,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
break :result try bitcast_result.toLocal(func, wanted_ty);
}
break :result func.reuseOperand(ty_op.operand, operand);
} else WValue{ .none = {} };
};
func.finishAir(inst, result, &.{ty_op.operand});
}
@ -3480,7 +3465,6 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn
fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.StructField, ty_pl.payload);
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.data.struct_operand});
const struct_ptr = try func.resolveInst(extra.data.struct_operand);
const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();
@ -3490,7 +3474,6 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const struct_ptr = try func.resolveInst(ty_op.operand);
const struct_ty = func.air.typeOf(ty_op.operand).childType();
@ -3535,7 +3518,6 @@ fn structFieldPtr(
fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
const struct_ty = func.air.typeOf(struct_field.struct_operand);
const operand = try func.resolveInst(struct_field.struct_operand);
@ -3801,7 +3783,6 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
const err_union_ty = func.air.typeOf(un_op);
const pl_ty = err_union_ty.errorUnionPayload();
@ -3836,7 +3817,6 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro
fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const op_ty = func.air.typeOf(ty_op.operand);
@ -3859,7 +3839,6 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo
fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const op_ty = func.air.typeOf(ty_op.operand);
@ -3883,7 +3862,6 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const err_ty = func.air.typeOfIndex(inst);
@ -3910,7 +3888,6 @@ fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void
fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const err_ty = func.air.getRefType(ty_op.ty);
@ -3937,7 +3914,6 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const ty = func.air.getRefType(ty_op.ty);
const operand = try func.resolveInst(ty_op.operand);
@ -4004,7 +3980,6 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
const op_ty = func.air.typeOf(un_op);
@ -4049,7 +4024,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
const opt_ty = func.air.typeOf(ty_op.operand);
const payload_ty = func.air.typeOfIndex(inst);
if (func.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
return func.finishAir(inst, .none, &.{ty_op.operand});
}
@ -4069,7 +4044,6 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const opt_ty = func.air.typeOf(ty_op.operand).childType();
@ -4114,7 +4088,6 @@ fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const payload_ty = func.air.typeOf(ty_op.operand);
const result = result: {
@ -4153,7 +4126,6 @@ fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const lhs = try func.resolveInst(bin_op.lhs);
const rhs = try func.resolveInst(bin_op.rhs);
@ -4168,7 +4140,6 @@ fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const len = try func.load(operand, Type.usize, func.ptrSize());
@ -4178,7 +4149,6 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const slice_ty = func.air.typeOf(bin_op.lhs);
const slice = try func.resolveInst(bin_op.lhs);
@ -4209,7 +4179,6 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const elem_ty = func.air.getRefType(ty_pl.ty).childType();
const elem_size = elem_ty.abiSize(func.target);
@ -4232,7 +4201,6 @@ fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const ptr = try func.load(operand, Type.usize, 0);
const result = try ptr.toLocal(func, Type.usize);
@ -4241,7 +4209,6 @@ fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const wanted_ty = func.air.getRefType(ty_op.ty);
@ -4270,19 +4237,14 @@ fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) Inner
fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
const result = if (func.liveness.isUnused(inst))
WValue{ .none = {} }
else result: {
const operand = try func.resolveInst(un_op);
break :result func.reuseOperand(un_op, operand);
};
const operand = try func.resolveInst(un_op);
const result = func.reuseOperand(un_op, operand);
func.finishAir(inst, result, &.{un_op});
}
fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const array_ty = func.air.typeOf(ty_op.operand).childType();
@ -4305,7 +4267,6 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
const result = switch (operand) {
@ -4318,7 +4279,6 @@ fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ptr_ty = func.air.typeOf(bin_op.lhs);
const ptr = try func.resolveInst(bin_op.lhs);
@ -4356,7 +4316,6 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ptr_ty = func.air.typeOf(bin_op.lhs);
const elem_ty = func.air.getRefType(ty_pl.ty).childType();
@ -4386,7 +4345,6 @@ fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ptr = try func.resolveInst(bin_op.lhs);
const offset = try func.resolveInst(bin_op.rhs);
@ -4510,7 +4468,6 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo
fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const array_ty = func.air.typeOf(bin_op.lhs);
const array = try func.resolveInst(bin_op.lhs);
@ -4579,7 +4536,6 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const dest_ty = func.air.typeOfIndex(inst);
@ -4604,7 +4560,6 @@ fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const dest_ty = func.air.typeOfIndex(inst);
@ -4719,10 +4674,6 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const child_ty = inst_ty.childType();
const elem_size = child_ty.abiSize(func.target);
if (func.liveness.isUnused(inst)) {
return func.finishAir(inst, .none, &.{ extra.a, extra.b });
}
const module = func.bin_file.base.options.module.?;
// TODO: One of them could be by ref; handle in loop
if (isByRef(func.air.typeOf(extra.a), func.target) or isByRef(inst_ty, func.target)) {
@ -4788,7 +4739,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);
const result: WValue = result_value: {
if (func.liveness.isUnused(inst)) break :result_value WValue.none;
switch (result_ty.zigTypeTag()) {
.Array => {
const result = try func.allocStack(result_ty);
@ -4894,7 +4844,6 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.init});
const result = result: {
const union_ty = func.air.typeOfIndex(inst);
@ -4933,7 +4882,6 @@ fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const pl_op = func.air.instructions.items(.data)[inst].pl_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
const result = try func.allocLocal(func.air.typeOfIndex(inst));
try func.addLabel(.memory_size, pl_op.payload);
@ -4943,7 +4891,6 @@ fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {
const pl_op = func.air.instructions.items(.data)[inst].pl_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
const operand = try func.resolveInst(pl_op.operand);
const result = try func.allocLocal(func.air.typeOfIndex(inst));
@ -5055,7 +5002,6 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const un_ty = func.air.typeOf(ty_op.operand);
const tag_ty = func.air.typeOfIndex(inst);
@ -5075,7 +5021,6 @@ fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const dest_ty = func.air.typeOfIndex(inst);
const operand = try func.resolveInst(ty_op.operand);
@ -5121,7 +5066,6 @@ fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!
fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const dest_ty = func.air.typeOfIndex(inst);
const operand = try func.resolveInst(ty_op.operand);
@ -5162,7 +5106,6 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const err_set_ty = func.air.typeOf(ty_op.operand).childType();
const payload_ty = err_set_ty.errorUnionPayload();
@ -5177,8 +5120,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
);
const result = result: {
if (func.liveness.isUnused(inst)) break :result WValue{ .none = {} };
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
break :result func.reuseOperand(ty_op.operand, operand);
}
@ -5191,7 +5132,6 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi
fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});
const field_ptr = try func.resolveInst(extra.field_ptr);
const parent_ty = func.air.getRefType(ty_pl.ty).childType();
@ -5231,7 +5171,6 @@ fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const operand = try func.resolveInst(ty_op.operand);
const op_ty = func.air.typeOf(ty_op.operand);
@ -5276,7 +5215,6 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
// First retrieve the symbol index to the error name table
@ -5318,7 +5256,6 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const slice_ptr = try func.resolveInst(ty_op.operand);
const result = try func.buildPointerOffset(slice_ptr, offset, .new);
func.finishAir(inst, result, &.{ty_op.operand});
@ -5328,7 +5265,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
assert(op == .add or op == .sub);
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
const lhs_op = try func.resolveInst(extra.lhs);
const rhs_op = try func.resolveInst(extra.rhs);
@ -5471,7 +5407,6 @@ fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type,
fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
const lhs = try func.resolveInst(extra.lhs);
const rhs = try func.resolveInst(extra.rhs);
@ -5519,7 +5454,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
const lhs = try func.resolveInst(extra.lhs);
const rhs = try func.resolveInst(extra.rhs);
@ -5605,7 +5539,6 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ty = func.air.typeOfIndex(inst);
if (ty.zigTypeTag() == .Vector) {
@ -5637,8 +5570,6 @@ 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, pl_op.operand });
const ty = func.air.typeOfIndex(inst);
if (ty.zigTypeTag() == .Vector) {
@ -5671,7 +5602,6 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const ty = func.air.typeOf(ty_op.operand);
const result_ty = func.air.typeOfIndex(inst);
@ -5724,7 +5654,6 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const ty = func.air.typeOf(ty_op.operand);
const result_ty = func.air.typeOfIndex(inst);
@ -5892,7 +5821,6 @@ fn lowerTry(
fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_op = func.air.instructions.items(.data)[inst].ty_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
const ty = func.air.typeOfIndex(inst);
const operand = try func.resolveInst(ty_op.operand);
@ -5963,7 +5891,6 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ty = func.air.typeOfIndex(inst);
const lhs = try func.resolveInst(bin_op.lhs);
@ -5978,7 +5905,6 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ty = func.air.typeOfIndex(inst);
const lhs = try func.resolveInst(bin_op.lhs);
@ -6127,7 +6053,6 @@ fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
assert(op == .add or op == .sub);
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ty = func.air.typeOfIndex(inst);
const lhs = try func.resolveInst(bin_op.lhs);
@ -6240,7 +6165,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
const ty = func.air.typeOfIndex(inst);
const int_info = ty.intInfo(func.target);
@ -6399,7 +6323,6 @@ fn callIntrinsic(
fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
const operand = try func.resolveInst(un_op);
const enum_ty = func.air.typeOf(un_op);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4523,6 +4523,10 @@ pub const FuncGen = struct {
fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
const air_tags = self.air.instructions.items(.tag);
for (body, 0..) |inst, i| {
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
continue;
}
const opt_value: ?*llvm.Value = switch (air_tags[inst]) {
// zig fmt: off
.add => try self.airAdd(inst, false),
@ -5166,8 +5170,6 @@ pub const FuncGen = struct {
}
fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const list = try self.resolveInst(ty_op.operand);
const arg_ty = self.air.getRefType(ty_op.ty);
@ -5177,8 +5179,6 @@ pub const FuncGen = struct {
}
fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const src_list = try self.resolveInst(ty_op.operand);
const va_list_ty = self.air.getRefType(ty_op.ty);
@ -5226,8 +5226,6 @@ pub const FuncGen = struct {
}
fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const va_list_ty = self.air.typeOfIndex(inst);
const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
@ -5254,7 +5252,6 @@ pub const FuncGen = struct {
}
fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -5266,7 +5263,6 @@ pub const FuncGen = struct {
}
fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
@ -5281,8 +5277,6 @@ pub const FuncGen = struct {
}
fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const llvm_fn = try self.getCmpLtErrorsLenFunction();
@ -5650,9 +5644,6 @@ pub const FuncGen = struct {
}
fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand_ty = self.air.typeOf(ty_op.operand);
const array_ty = operand_ty.childType();
@ -5674,9 +5665,6 @@ pub const FuncGen = struct {
}
fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
@ -5733,9 +5721,6 @@ pub const FuncGen = struct {
}
fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
self.builder.setFastMath(want_fast_math);
const target = self.dg.module.getTarget();
@ -5792,16 +5777,12 @@ pub const FuncGen = struct {
}
fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
return self.builder.buildExtractValue(operand, index, "");
}
fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const slice_ptr = try self.resolveInst(ty_op.operand);
const slice_ptr_ty = self.air.typeOf(ty_op.operand);
@ -5814,8 +5795,6 @@ pub const FuncGen = struct {
const inst = body_tail[0];
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const slice_ty = self.air.typeOf(bin_op.lhs);
if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
const slice = try self.resolveInst(bin_op.lhs);
const index = try self.resolveInst(bin_op.rhs);
const elem_ty = slice_ty.childType();
@ -5835,7 +5814,6 @@ pub const FuncGen = struct {
}
fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const slice_ty = self.air.typeOf(bin_op.lhs);
@ -5850,7 +5828,6 @@ pub const FuncGen = struct {
fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
const inst = body_tail[0];
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const array_ty = self.air.typeOf(bin_op.lhs);
@ -5881,8 +5858,6 @@ pub const FuncGen = struct {
const inst = body_tail[0];
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const ptr_ty = self.air.typeOf(bin_op.lhs);
if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
const elem_ty = ptr_ty.childType();
const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);
const base_ptr = try self.resolveInst(bin_op.lhs);
@ -5908,8 +5883,6 @@ pub const FuncGen = struct {
}
fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const ptr_ty = self.air.typeOf(bin_op.lhs);
@ -5934,9 +5907,6 @@ pub const FuncGen = struct {
}
fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
const struct_ptr = try self.resolveInst(struct_field.struct_operand);
@ -5949,8 +5919,6 @@ pub const FuncGen = struct {
inst: Air.Inst.Index,
field_index: u32,
) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const struct_ptr = try self.resolveInst(ty_op.operand);
const struct_ptr_ty = self.air.typeOf(ty_op.operand);
@ -5959,8 +5927,6 @@ pub const FuncGen = struct {
fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
const inst = body_tail[0];
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
const struct_ty = self.air.typeOf(struct_field.struct_operand);
@ -6060,8 +6026,6 @@ pub const FuncGen = struct {
}
fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
@ -6083,9 +6047,6 @@ pub const FuncGen = struct {
}
fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
@ -6263,8 +6224,6 @@ pub const FuncGen = struct {
const clobbers_len = @truncate(u31, extra.data.flags);
var extra_i: usize = extra.end;
if (!is_volatile and self.liveness.isUnused(inst)) return null;
const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
extra_i += outputs.len;
const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
@ -6610,8 +6569,6 @@ pub const FuncGen = struct {
operand_is_ptr: bool,
pred: llvm.IntPredicate,
) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const operand_ty = self.air.typeOf(un_op);
@ -6659,8 +6616,6 @@ pub const FuncGen = struct {
op: llvm.IntPredicate,
operand_is_ptr: bool,
) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const operand_ty = self.air.typeOf(un_op);
@ -6701,8 +6656,6 @@ pub const FuncGen = struct {
}
fn airOptionalPayloadPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const optional_ty = self.air.typeOf(ty_op.operand).childType();
@ -6756,8 +6709,6 @@ pub const FuncGen = struct {
fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
const inst = body_tail[0];
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const optional_ty = self.air.typeOf(ty_op.operand);
@ -6780,8 +6731,6 @@ pub const FuncGen = struct {
operand_is_ptr: bool,
) !?*llvm.Value {
const inst = body_tail[0];
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
@ -6817,9 +6766,6 @@ pub const FuncGen = struct {
inst: Air.Inst.Index,
operand_is_ptr: bool,
) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
@ -6893,8 +6839,6 @@ pub const FuncGen = struct {
}
fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.module.getTarget();
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
@ -6911,8 +6855,6 @@ pub const FuncGen = struct {
}
fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const payload_ty = self.air.typeOf(ty_op.operand);
const non_null_bit = self.context.intType(8).constInt(1, .False);
@ -6943,8 +6885,6 @@ pub const FuncGen = struct {
}
fn airWrapErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const err_un_ty = self.air.typeOfIndex(inst);
const operand = try self.resolveInst(ty_op.operand);
@ -6978,8 +6918,6 @@ pub const FuncGen = struct {
}
fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const err_un_ty = self.air.typeOfIndex(inst);
const payload_ty = err_un_ty.errorUnionPayload();
@ -7015,8 +6953,6 @@ pub const FuncGen = struct {
}
fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const index = pl_op.payload;
const llvm_u32 = self.context.intType(32);
@ -7061,8 +6997,6 @@ pub const FuncGen = struct {
}
fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7074,8 +7008,6 @@ pub const FuncGen = struct {
}
fn airMax(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7087,8 +7019,6 @@ pub const FuncGen = struct {
}
fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const ptr = try self.resolveInst(bin_op.lhs);
@ -7103,7 +7033,6 @@ pub const FuncGen = struct {
}
fn airAdd(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7118,7 +7047,6 @@ pub const FuncGen = struct {
}
fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7129,8 +7057,6 @@ pub const FuncGen = struct {
}
fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7144,7 +7070,6 @@ pub const FuncGen = struct {
}
fn airSub(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7159,7 +7084,6 @@ pub const FuncGen = struct {
}
fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7170,8 +7094,6 @@ pub const FuncGen = struct {
}
fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7184,7 +7106,6 @@ pub const FuncGen = struct {
}
fn airMul(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7199,7 +7120,6 @@ pub const FuncGen = struct {
}
fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7210,8 +7130,6 @@ pub const FuncGen = struct {
}
fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7224,7 +7142,6 @@ pub const FuncGen = struct {
}
fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7236,7 +7153,6 @@ pub const FuncGen = struct {
}
fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7254,7 +7170,6 @@ pub const FuncGen = struct {
}
fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7287,7 +7202,6 @@ pub const FuncGen = struct {
}
fn airDivExact(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7302,7 +7216,6 @@ pub const FuncGen = struct {
}
fn airRem(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7317,7 +7230,6 @@ pub const FuncGen = struct {
}
fn airMod(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@ -7347,8 +7259,6 @@ pub const FuncGen = struct {
}
fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const base_ptr = try self.resolveInst(bin_op.lhs);
@ -7368,8 +7278,6 @@ pub const FuncGen = struct {
}
fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const base_ptr = try self.resolveInst(bin_op.lhs);
@ -7395,9 +7303,6 @@ pub const FuncGen = struct {
signed_intrinsic: []const u8,
unsigned_intrinsic: []const u8,
) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
@ -7686,8 +7591,6 @@ pub const FuncGen = struct {
}
fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
@ -7700,9 +7603,6 @@ pub const FuncGen = struct {
}
fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
@ -7759,8 +7659,6 @@ pub const FuncGen = struct {
}
fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7768,8 +7666,6 @@ pub const FuncGen = struct {
}
fn airOr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7777,8 +7673,6 @@ pub const FuncGen = struct {
}
fn airXor(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@ -7786,8 +7680,6 @@ pub const FuncGen = struct {
}
fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@ -7809,8 +7701,6 @@ pub const FuncGen = struct {
}
fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@ -7831,8 +7721,6 @@ pub const FuncGen = struct {
}
fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@ -7876,8 +7764,6 @@ pub const FuncGen = struct {
}
fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@ -7912,9 +7798,6 @@ pub const FuncGen = struct {
}
fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const target = self.dg.module.getTarget();
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const dest_ty = self.air.typeOfIndex(inst);
@ -7937,8 +7820,6 @@ pub const FuncGen = struct {
}
fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
@ -7946,9 +7827,6 @@ pub const FuncGen = struct {
}
fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
@ -7978,9 +7856,6 @@ pub const FuncGen = struct {
}
fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
@ -8010,9 +7885,6 @@ pub const FuncGen = struct {
}
fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
@ -8020,8 +7892,6 @@ pub const FuncGen = struct {
}
fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand_ty = self.air.typeOf(ty_op.operand);
const inst_ty = self.air.typeOfIndex(inst);
@ -8137,9 +8007,6 @@ pub const FuncGen = struct {
}
fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
return operand;
@ -8189,7 +8056,6 @@ pub const FuncGen = struct {
}
fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ptr_ty = self.air.typeOfIndex(inst);
const pointee_type = ptr_ty.childType();
if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
@ -8201,7 +8067,6 @@ pub const FuncGen = struct {
}
fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ptr_ty = self.air.typeOfIndex(inst);
const ret_ty = ptr_ty.childType();
if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty);
@ -8289,8 +8154,6 @@ pub const FuncGen = struct {
const ptr = try fg.resolveInst(ty_op.operand);
elide: {
if (ptr_info.@"volatile") break :elide;
if (fg.liveness.isUnused(inst)) return null;
if (!isByRef(ptr_info.pointee_type)) break :elide;
if (!canElideLoad(fg, body_tail)) break :elide;
return ptr;
@ -8314,8 +8177,7 @@ pub const FuncGen = struct {
}
fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
_ = inst;
const llvm_usize = try self.dg.lowerType(Type.usize);
const target = self.dg.module.getTarget();
if (!target_util.supportsReturnAddress(target)) {
@ -8331,8 +8193,7 @@ pub const FuncGen = struct {
}
fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
_ = inst;
const llvm_i32 = self.context.intType(32);
const llvm_fn_name = "llvm.frameaddress.p0";
const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
@ -8462,8 +8323,6 @@ pub const FuncGen = struct {
const ptr = try self.resolveInst(atomic_load.ptr);
const ptr_ty = self.air.typeOf(atomic_load.ptr);
const ptr_info = ptr_ty.ptrInfo().data;
if (!ptr_info.@"volatile" and self.liveness.isUnused(inst))
return null;
const elem_ty = ptr_info.pointee_type;
if (!elem_ty.hasRuntimeBitsIgnoreComptime())
return null;
@ -8577,8 +8436,6 @@ pub const FuncGen = struct {
}
fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const un_ty = self.air.typeOf(ty_op.operand);
const target = self.dg.module.getTarget();
@ -8603,8 +8460,6 @@ pub const FuncGen = struct {
}
fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const operand_ty = self.air.typeOf(un_op);
@ -8613,7 +8468,6 @@ pub const FuncGen = struct {
}
fn airNeg(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const un_op = self.air.instructions.items(.data)[inst].un_op;
@ -8624,8 +8478,6 @@ pub const FuncGen = struct {
}
fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand_ty = self.air.typeOf(ty_op.operand);
const operand = try self.resolveInst(ty_op.operand);
@ -8652,8 +8504,6 @@ pub const FuncGen = struct {
}
fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand_ty = self.air.typeOf(ty_op.operand);
const operand = try self.resolveInst(ty_op.operand);
@ -8679,8 +8529,6 @@ pub const FuncGen = struct {
}
fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.module.getTarget();
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand_ty = self.air.typeOf(ty_op.operand);
@ -8734,8 +8582,6 @@ pub const FuncGen = struct {
}
fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const error_set_ty = self.air.getRefType(ty_op.ty);
@ -8781,8 +8627,6 @@ pub const FuncGen = struct {
}
fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const enum_ty = self.air.typeOf(un_op);
@ -8862,8 +8706,6 @@ pub const FuncGen = struct {
}
fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const enum_ty = self.air.typeOf(un_op);
@ -8995,8 +8837,6 @@ pub const FuncGen = struct {
}
fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const slice_ty = self.air.typeOfIndex(inst);
@ -9011,8 +8851,6 @@ pub const FuncGen = struct {
}
fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const scalar = try self.resolveInst(ty_op.operand);
const vector_ty = self.air.typeOfIndex(inst);
@ -9021,8 +8859,6 @@ pub const FuncGen = struct {
}
fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const pred = try self.resolveInst(pl_op.operand);
@ -9033,8 +8869,6 @@ pub const FuncGen = struct {
}
fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
const a = try self.resolveInst(extra.a);
@ -9134,7 +8968,6 @@ pub const FuncGen = struct {
}
fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
const target = self.dg.module.getTarget();
@ -9221,8 +9054,6 @@ pub const FuncGen = struct {
}
fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const result_ty = self.air.typeOfIndex(inst);
const len = @intCast(usize, result_ty.arrayLen());
@ -9360,8 +9191,6 @@ pub const FuncGen = struct {
}
fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
const union_ty = self.air.typeOfIndex(inst);
@ -9566,8 +9395,6 @@ pub const FuncGen = struct {
}
fn airAddrSpaceCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const inst_ty = self.air.typeOfIndex(inst);
const operand = try self.resolveInst(ty_op.operand);
@ -9592,8 +9419,6 @@ pub const FuncGen = struct {
}
fn airWorkItemId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.module.getTarget();
assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
@ -9603,8 +9428,6 @@ pub const FuncGen = struct {
}
fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.module.getTarget();
assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
@ -9634,8 +9457,6 @@ pub const FuncGen = struct {
}
fn airWorkGroupId(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.module.getTarget();
assert(target.cpu.arch == .amdgcn); // TODO is to port this function to other GPU architectures
@ -9756,8 +9577,6 @@ pub const FuncGen = struct {
struct_ptr_ty: Type,
field_index: u32,
) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const target = self.dg.object.target;
const struct_ty = struct_ptr_ty.childType();
switch (struct_ty.zigTypeTag()) {

View File

@ -1507,6 +1507,11 @@ pub const DeclGen = struct {
}
fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
// TODO: remove now-redundant isUnused calls from AIR handler functions
if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) {
return;
}
const air_tags = self.air.instructions.items(.tag);
const maybe_result_id: ?IdRef = switch (air_tags[inst]) {
// zig fmt: off

View File

@ -8,16 +8,16 @@ const Type = @import("type.zig").Type;
const Air = @import("Air.zig");
const Liveness = @import("Liveness.zig");
pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) void {
pub fn write(stream: anytype, module: *Module, air: Air, liveness: ?Liveness) void {
const instruction_bytes = air.instructions.len *
// Here we don't use @sizeOf(Air.Inst.Data) because it would include
// the debug safety tag but we want to measure release size.
(@sizeOf(Air.Inst.Tag) + 8);
const extra_bytes = air.extra.len * @sizeOf(u32);
const values_bytes = air.values.len * @sizeOf(Value);
const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);
const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);
const liveness_special_bytes = liveness.special.count() * 8;
const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0;
const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0;
const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0;
const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
values_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
liveness_special_bytes + tomb_bytes;
@ -38,8 +38,8 @@ pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) voi
air.extra.len, fmtIntSizeBin(extra_bytes),
air.values.len, fmtIntSizeBin(values_bytes),
fmtIntSizeBin(tomb_bytes),
liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
if (liveness) |l| l.extra.len else 0, fmtIntSizeBin(liveness_extra_bytes),
if (liveness) |l| l.special.count() else 0, fmtIntSizeBin(liveness_special_bytes),
}) catch return;
// zig fmt: on
@ -61,7 +61,7 @@ pub fn writeInst(
inst: Air.Inst.Index,
module: *Module,
air: Air,
liveness: Liveness,
liveness: ?Liveness,
) void {
var writer: Writer = .{
.module = module,
@ -74,11 +74,11 @@ pub fn writeInst(
writer.writeInst(stream, inst) catch return;
}
pub fn dump(module: *Module, air: Air, liveness: Liveness) void {
pub fn dump(module: *Module, air: Air, liveness: ?Liveness) void {
write(std.io.getStdErr().writer(), module, air, liveness);
}
pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: Liveness) void {
pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: ?Liveness) void {
writeInst(std.io.getStdErr().writer(), inst, module, air, liveness);
}
@ -86,7 +86,7 @@ const Writer = struct {
module: *Module,
gpa: Allocator,
air: Air,
liveness: Liveness,
liveness: ?Liveness,
indent: usize,
skip_body: bool,
@ -109,7 +109,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
try s.print("%{d}{c}= {s}(", .{
inst,
@as(u8, if (w.liveness.isUnused(inst)) '!' else ' '),
@as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
@tagName(tag),
});
switch (tag) {
@ -389,6 +389,10 @@ const Writer = struct {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const extra = w.air.extraData(Air.Block, ty_pl.payload);
const body = w.air.extra[extra.end..][0..extra.data.body_len];
const liveness_block = if (w.liveness) |liveness|
liveness.getBlock(inst)
else
Liveness.BlockSlices{ .deaths = &.{} };
try w.writeType(s, w.air.getRefType(ty_pl.ty));
if (w.skip_body) return s.writeAll(", ...");
@ -399,13 +403,16 @@ const Writer = struct {
w.indent = old_indent;
try s.writeByteNTimes(' ', w.indent);
try s.writeAll("}");
for (liveness_block.deaths) |operand| {
try s.print(" %{d}!", .{operand});
}
}
fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const extra = w.air.extraData(Air.Block, ty_pl.payload);
const body = w.air.extra[extra.end..][0..extra.data.body_len];
const liveness_loop = w.liveness.getLoop(inst);
try w.writeType(s, w.air.getRefType(ty_pl.ty));
if (w.skip_body) return s.writeAll(", ...");
@ -413,14 +420,6 @@ const Writer = struct {
const old_indent = w.indent;
w.indent += 2;
try w.writeBody(s, body);
if (liveness_loop.deaths.len != 0) {
try s.writeByteNTimes(' ', w.indent);
for (liveness_loop.deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{operand});
}
try s.writeAll("\n");
}
w.indent = old_indent;
try s.writeByteNTimes(' ', w.indent);
try s.writeAll("}");
@ -746,22 +745,44 @@ const Writer = struct {
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
const extra = w.air.extraData(Air.Try, pl_op.payload);
const body = w.air.extra[extra.end..][0..extra.data.body_len];
const liveness_condbr = if (w.liveness) |liveness|
liveness.getCondBr(inst)
else
Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
try w.writeOperand(s, inst, 0, pl_op.operand);
if (w.skip_body) return s.writeAll(", ...");
try s.writeAll(", {\n");
const old_indent = w.indent;
w.indent += 2;
if (liveness_condbr.else_deaths.len != 0) {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{operand});
}
try s.writeAll("\n");
}
try w.writeBody(s, body);
w.indent = old_indent;
try s.writeByteNTimes(' ', w.indent);
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{operand});
}
}
fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
const body = w.air.extra[extra.end..][0..extra.data.body_len];
const liveness_condbr = if (w.liveness) |liveness|
liveness.getCondBr(inst)
else
Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
try w.writeOperand(s, inst, 0, extra.data.ptr);
@ -771,10 +792,24 @@ const Writer = struct {
try s.writeAll(", {\n");
const old_indent = w.indent;
w.indent += 2;
if (liveness_condbr.else_deaths.len != 0) {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{operand});
}
try s.writeAll("\n");
}
try w.writeBody(s, body);
w.indent = old_indent;
try s.writeByteNTimes(' ', w.indent);
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{operand});
}
}
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
@ -782,7 +817,10 @@ const Writer = struct {
const extra = w.air.extraData(Air.CondBr, pl_op.payload);
const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
const liveness_condbr = w.liveness.getCondBr(inst);
const liveness_condbr = if (w.liveness) |liveness|
liveness.getCondBr(inst)
else
Liveness.CondBrSlices{ .then_deaths = &.{}, .else_deaths = &.{} };
try w.writeOperand(s, inst, 0, pl_op.operand);
if (w.skip_body) return s.writeAll(", ...");
@ -822,8 +860,15 @@ const Writer = struct {
fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);
const liveness = w.liveness.getSwitchBr(w.gpa, inst, switch_br.data.cases_len + 1) catch
@panic("out of memory");
const liveness = if (w.liveness) |liveness|
liveness.getSwitchBr(w.gpa, inst, switch_br.data.cases_len + 1) catch
@panic("out of memory")
else blk: {
const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.data.cases_len + 1) catch
@panic("out of memory");
std.mem.set([]const Air.Inst.Index, slice, &.{});
break :blk Liveness.SwitchBrTable{ .deaths = slice };
};
defer w.gpa.free(liveness.deaths);
var extra_index: usize = switch_br.end;
var case_i: u32 = 0;
@ -913,13 +958,13 @@ const Writer = struct {
operand: Air.Inst.Ref,
) @TypeOf(s).Error!void {
const small_tomb_bits = Liveness.bpi - 1;
const dies = if (op_index < small_tomb_bits)
w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))
else blk: {
var extra_index = w.liveness.special.get(inst).?;
const dies = if (w.liveness) |liveness| blk: {
if (op_index < small_tomb_bits)
break :blk liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index));
var extra_index = liveness.special.get(inst).?;
var tomb_op_index: usize = small_tomb_bits;
while (true) {
const bits = w.liveness.extra[extra_index];
const bits = liveness.extra[extra_index];
if (op_index < tomb_op_index + 31) {
break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0;
}
@ -927,7 +972,7 @@ const Writer = struct {
extra_index += 1;
tomb_op_index += 31;
}
};
} else false;
return w.writeInstRef(s, operand, dies);
}

View File

@ -95,6 +95,10 @@ pub fn RegisterManager(
return indexOfReg(tracked_registers, reg);
}
pub fn regAtTrackedIndex(index: RegisterBitSet.ShiftInt) Register {
return tracked_registers[index];
}
/// Returns true when this register is not tracked
pub fn isRegFree(self: Self, reg: Register) bool {
const index = indexOfRegIntoTracked(reg) orelse return true;

View File

@ -274,7 +274,6 @@ test "two counters" {
test "1-based counter and ptr to array" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
var ok: usize = 0;

View File

@ -215,3 +215,29 @@ test "copy VaList" {
try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
}
test "unused VaList arg" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
const S = struct {
fn thirdArg(dummy: c_int, ...) callconv(.C) c_int {
_ = dummy;
var ap = @cVaStart();
defer @cVaEnd(&ap);
_ = @cVaArg(&ap, c_int);
return @cVaArg(&ap, c_int);
}
};
const x = S.thirdArg(0, @as(c_int, 1), @as(c_int, 2));
try std.testing.expectEqual(@as(c_int, 2), x);
}

View File

@ -1,26 +0,0 @@
pub fn main() void {
foo(123);
}
fn foo(x: u64) void {
if (x > 42) {
print();
}
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{x8}" (64),
[arg1] "{x0}" (1),
[arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{x2}" ("Hello, World!\n".len),
: "memory", "cc"
);
}
// run
// target=aarch64-linux
//
// Hello, World!
//

View File

@ -1,25 +0,0 @@
pub fn main() void {
foo(true);
}
fn foo(x: bool) void {
if (x) {
print();
}
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{x8}" (64),
[arg1] "{x0}" (1),
[arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{x2}" ("Hello, World!\n".len),
: "memory", "cc"
);
}
// run
//
// Hello, World!
//

View File

@ -1,31 +0,0 @@
pub export fn _start() noreturn {
print();
exit(0);
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{x8}" (64),
[arg1] "{x0}" (1),
[arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{x2}" ("Hello, World!\n".len),
: "memory", "cc"
);
}
fn exit(ret: usize) noreturn {
asm volatile ("svc #0"
:
: [number] "{x8}" (93),
[arg1] "{x0}" (ret),
: "memory", "cc"
);
unreachable;
}
// run
// target=aarch64-linux
//
// Hello, World!
//

View File

@ -1,36 +0,0 @@
pub export fn _start() noreturn {
print();
print();
print();
print();
exit(0);
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{x8}" (64),
[arg1] "{x0}" (1),
[arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{x2}" ("Hello, World!\n".len),
: "memory", "cc"
);
}
fn exit(ret: usize) noreturn {
asm volatile ("svc #0"
:
: [number] "{x8}" (93),
[arg1] "{x0}" (ret),
: "memory", "cc"
);
unreachable;
}
// run
//
// Hello, World!
// Hello, World!
// Hello, World!
// Hello, World!
//

View File

@ -1,21 +0,0 @@
pub fn main() void {
print();
print();
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{x8}" (64),
[arg1] "{x0}" (1),
[arg2] "{x1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{x2}" ("Hello, World!\n".len),
: "memory", "cc"
);
}
// run
//
// Hello, World!
// Hello, World!
//

View File

@ -1,22 +0,0 @@
extern "c" fn write(usize, usize, usize) usize;
pub fn main() void {
print();
print();
print();
print();
}
fn print() void {
const msg = @ptrToInt("Hello, World!\n");
const len = 14;
_ = write(1, msg, len);
}
// run
//
// Hello, World!
// Hello, World!
// Hello, World!
// Hello, World!
//

View File

@ -1,16 +0,0 @@
extern "c" fn write(usize, usize, usize) usize;
pub fn main() void {
print();
}
fn print() void {
const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
const len = 104;
_ = write(1, msg, len);
}
// run
//
// What is up? This is a longer message that will force the data to be relocated in virtual address space.
//

View File

@ -1,18 +0,0 @@
extern "c" fn write(usize, usize, usize) usize;
pub fn main() void {
print();
print();
}
fn print() void {
const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n");
const len = 104;
_ = write(1, msg, len);
}
// run
//
// What is up? This is a longer message that will force the data to be relocated in virtual address space.
// What is up? This is a longer message that will force the data to be relocated in virtual address space.
//

View File

@ -0,0 +1,17 @@
const std = @import("std");
pub fn main() void {
print(2, 4);
print(1, 7);
}
fn print(a: u32, b: u32) void {
const str = "123456789";
const len = a + b;
_ = std.os.write(1, str[0..len]) catch {};
}
// run
// target=x86_64-linux,x86_64-macos
//
// 12345612345678

View File

@ -0,0 +1,16 @@
const std = @import("std");
pub fn main() void {
print(10, 5);
print(4, 3);
}
fn print(a: u32, b: u32) void {
const str = "123456789";
const len = a - b;
_ = std.os.write(1, str[0..len]) catch {};
}
// run
//
// 123451

View File

@ -0,0 +1,16 @@
const std = @import("std");
pub fn main() void {
print(8, 9);
print(3, 7);
}
fn print(a: u32, b: u32) void {
const str = "123456789";
const len = a & b;
_ = std.os.write(1, str[0..len]) catch {};
}
// run
//
// 12345678123

View File

@ -0,0 +1,16 @@
const std = @import("std");
pub fn main() void {
print(4, 2);
print(3, 7);
}
fn print(a: u32, b: u32) void {
const str = "123456789";
const len = a | b;
_ = std.os.write(1, str[0..len]) catch {};
}
// run
//
// 1234561234567

View File

@ -0,0 +1,16 @@
const std = @import("std");
pub fn main() void {
print(42, 42);
print(3, 5);
}
fn print(a: u32, b: u32) void {
const str = "123456789";
const len = a ^ b;
_ = std.os.write(1, str[0..len]) catch {};
}
// run
//
// 123456

View File

@ -1,21 +0,0 @@
pub fn main() void {
print(2, 4);
print(1, 7);
}
fn print(a: u32, b: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (a + b),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("123456789")),
: "memory"
);
return;
}
// run
// target=arm-linux
//
// 12345612345678

View File

@ -1,20 +0,0 @@
pub fn main() void {
print(10, 5);
print(4, 3);
}
fn print(a: u32, b: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (a - b),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("123456789")),
: "memory"
);
return;
}
// run
//
// 123451

View File

@ -1,20 +0,0 @@
pub fn main() void {
print(8, 9);
print(3, 7);
}
fn print(a: u32, b: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (a & b),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("123456789")),
: "memory"
);
return;
}
// run
//
// 12345678123

View File

@ -1,20 +0,0 @@
pub fn main() void {
print(4, 2);
print(3, 7);
}
fn print(a: u32, b: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (a | b),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("123456789")),
: "memory"
);
return;
}
// run
//
// 1234561234567

View File

@ -1,20 +0,0 @@
pub fn main() void {
print(42, 42);
print(3, 5);
}
fn print(a: u32, b: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (a ^ b),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("123456789")),
: "memory"
);
return;
}
// run
//
// 123456

View File

@ -1,21 +0,0 @@
pub fn main() void {
foo() catch print();
}
fn foo() anyerror!void {}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{r2}" ("Hello, World!\n".len),
: "memory"
);
return;
}
// run
// target=arm-linux
//

View File

@ -1,24 +0,0 @@
pub fn main() void {
foo() catch print();
}
fn foo() anyerror!void {
return error.Test;
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{r2}" ("Hello, World!\n".len),
: "memory"
);
return;
}
// run
//
// Hello, World!
//

View File

@ -1,44 +0,0 @@
const PrintFn = *const fn () void;
pub fn main() void {
var printFn: PrintFn = stopSayingThat;
var i: u32 = 0;
while (i < 4) : (i += 1) printFn();
printFn = moveEveryZig;
printFn();
}
fn stopSayingThat() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n")),
[arg3] "{r2}" ("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n".len),
: "memory"
);
return;
}
fn moveEveryZig() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("All your codebase are belong to us\n")),
[arg3] "{r2}" ("All your codebase are belong to us\n".len),
: "memory"
);
return;
}
// run
// target=arm-linux
//
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// All your codebase are belong to us
//

View File

@ -1,32 +0,0 @@
pub export fn _start() noreturn {
print();
exit();
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{r2}" (14),
: "memory"
);
return;
}
fn exit() noreturn {
asm volatile ("svc #0"
:
: [number] "{r7}" (1),
[arg1] "{r0}" (0),
: "memory"
);
unreachable;
}
// run
// target=arm-linux
//
// Hello, World!
//

View File

@ -1,37 +0,0 @@
pub export fn _start() noreturn {
print();
print();
print();
print();
exit();
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{r2}" (14),
: "memory"
);
return;
}
fn exit() noreturn {
asm volatile ("svc #0"
:
: [number] "{r7}" (1),
[arg1] "{r0}" (0),
: "memory"
);
unreachable;
}
// run
//
// Hello, World!
// Hello, World!
// Hello, World!
// Hello, World!
//

View File

@ -1,22 +0,0 @@
pub fn main() void {
print();
print();
}
fn print() void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
[arg3] "{r2}" (14),
: "memory"
);
return;
}
// run
//
// Hello, World!
// Hello, World!
//

View File

@ -1,28 +0,0 @@
pub fn main() void {
print(id(14));
}
fn id(x: u32) u32 {
return x;
}
// TODO: The parameters to the asm statement in print() had to
// be in a specific order because otherwise the write to r0
// would overwrite the len parameter which resides in r0
fn print(len: u32) void {
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg3] "{r2}" (len),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
: "memory"
);
return;
}
// run
// target=arm-linux
//
// Hello, World!
//

View File

@ -1,40 +0,0 @@
pub fn main() void {
printNumberHex(0x00000000);
printNumberHex(0xaaaaaaaa);
printNumberHex(0xdeadbeef);
printNumberHex(0x31415926);
}
fn printNumberHex(x: u32) void {
var i: u5 = 28;
while (true) : (i -= 4) {
const digit = (x >> i) & 0xf;
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit),
[arg3] "{r2}" (1),
: "memory"
);
if (i == 0) break;
}
asm volatile ("svc #0"
:
: [number] "{r7}" (4),
[arg1] "{r0}" (1),
[arg2] "{r1}" (@ptrToInt("\n")),
[arg3] "{r2}" (1),
: "memory"
);
}
// run
// target=arm-linux
//
// 00000000
// aaaaaaaa
// deadbeef
// 31415926
//

View File

@ -11,5 +11,5 @@ pub fn assert(ok: bool) void {
}
// run
// target=x86_64-linux
//
// target=x86_64-macos,x86_64-linux
// link_libc=true

View File

@ -0,0 +1,20 @@
const builtin = @import("builtin");
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
for ("hello") |_| print();
}
fn print() void {
_ = write(1, @ptrToInt("hello\n"), 6);
}
// run
//
// hello
// hello
// hello
// hello
// hello
//

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
var i: u32 = 0;

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
var i: u32 = 0;

View File

@ -5,5 +5,5 @@ pub fn main() void {
}
// run
// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos
// target=x86_64-linux,x86_64-macos
//

View File

@ -6,7 +6,8 @@ pub fn main() void {
// error
// output_mode=Exe
// target=x86_64-linux
// target=x86_64-macos,x86_64-linux
// link_libc=true
//
// :4:19: error: store to comptime variable depends on runtime condition
// :4:11: note: runtime condition here

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
comptime var len: u32 = 5;

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
comptime var i: u64 = 2;

View File

@ -0,0 +1,23 @@
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
foo(123);
}
fn foo(x: u64) void {
if (x > 42) {
print();
}
}
fn print() void {
const str = "Hello, World!\n";
_ = write(1, @ptrToInt(str.ptr), ptr.len);
}
// run
// target=x86_64-linux,x86_64-macos
// link_libc=true
//
// Hello, World!
//

View File

@ -0,0 +1,25 @@
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
foo(true);
}
fn foo(x: bool) void {
if (x) {
print();
print();
} else {
print();
}
}
fn print() void {
const str = "Hello, World!\n";
_ = write(1, @ptrToInt(str.ptr), ptr.len);
}
// run
//
// Hello, World!
// Hello, World!
//

15
test/cases/errors.0.zig Normal file
View File

@ -0,0 +1,15 @@
const std = @import("std");
pub fn main() void {
foo() catch print();
}
fn foo() anyerror!void {}
fn print() void {
_ = std.os.write(1, "Hello, World!\n") catch {};
}
// run
// target=x86_64-macos
//

18
test/cases/errors.1.zig Normal file
View File

@ -0,0 +1,18 @@
const std = @import("std");
pub fn main() void {
foo() catch print();
}
fn foo() anyerror!void {
return error.Test;
}
fn print() void {
_ = std.os.write(1, "Hello, World!\n") catch {};
}
// run
//
// Hello, World!
//

5
test/cases/exit.zig Normal file
View File

@ -0,0 +1,5 @@
pub fn main() void {}
// run
// target=x86_64-linux,x86_64-macos,x86_64-windows,x86_64-plan9
//

View File

@ -0,0 +1,30 @@
const std = @import("std");
const PrintFn = *const fn () void;
pub fn main() void {
var printFn: PrintFn = stopSayingThat;
var i: u32 = 0;
while (i < 4) : (i += 1) printFn();
printFn = moveEveryZig;
printFn();
}
fn stopSayingThat() void {
_ = std.os.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};
}
fn moveEveryZig() void {
_ = std.os.write(1, "All your codebase are belong to us\n") catch {};
}
// run
// target=x86_64-macos
//
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// All your codebase are belong to us
//

View File

@ -1,5 +1,6 @@
// error
// output_mode=Exe
// target=aarch64-macos
// target=x86_64-linux,x86_64-macos
// link_libc=true
//
// :?:?: error: root struct of file 'tmp' has no member named 'main'

View File

@ -1,5 +1,5 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn exit(usize) noreturn;
extern "c" fn write(c_int, usize, usize) usize;
extern "c" fn exit(c_int) noreturn;
pub export fn main() noreturn {
print();

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
print();

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
print();

View File

@ -1,4 +1,4 @@
extern "c" fn write(usize, usize, usize) usize;
extern "c" fn write(c_int, usize, usize) usize;
pub fn main() void {
print();

Some files were not shown because too many files have changed in this diff Show More