mirror of
https://github.com/ziglang/zig.git
synced 2026-02-09 11:03:30 +00:00
This safety check was completely broken; it triggered unchecked illegal behavior *in order to implement the safety check*. You definitely can't do that! Instead, we must explicitly check the boundaries. This is a tiny bit fiddly, because we need to make sure we do floating-point rounding in the correct direction, and also handle the fact that the operation truncates so the boundary works differently for min vs max. Instead of implementing this safety check in Sema, there are now dedicated AIR instructions for safety-checked intfromfloat (two instructions; which one is used depends on the float mode). Currently, no backend directly implements them; instead, a `Legalize.Feature` is added which expands the safety check, and this feature is enabled for all backends we currently test, including the LLVM backend. The `u0` case is still handled in Sema, because Sema needs to check for that anyway due to the comptime-known result. The old safety check here was also completely broken and has therefore been rewritten. In that case, we just check for 'abs(input) < 1.0'. I've added a bunch of test coverage for the boundary cases of `@intFromFloat`, both for successes (in `test/behavior/cast.zig`) and failures (in `test/cases/safety/`). Resolves: #24161
1041 lines
39 KiB
Zig
1041 lines
39 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
|
|
|
|
const build_options = @import("build_options");
|
|
const Zcu = @import("../Zcu.zig");
|
|
const Value = @import("../Value.zig");
|
|
const Type = @import("../Type.zig");
|
|
const Air = @import("../Air.zig");
|
|
const InternPool = @import("../InternPool.zig");
|
|
|
|
pub fn write(air: Air, stream: anytype, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
|
|
comptime std.debug.assert(build_options.enable_debug_extensions);
|
|
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.items.len * @sizeOf(u32);
|
|
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 +
|
|
@sizeOf(Air.Liveness) + liveness_extra_bytes +
|
|
liveness_special_bytes + tomb_bytes;
|
|
|
|
// zig fmt: off
|
|
stream.print(
|
|
\\# Total AIR+Liveness bytes: {}
|
|
\\# AIR Instructions: {d} ({})
|
|
\\# AIR Extra Data: {d} ({})
|
|
\\# Liveness tomb_bits: {}
|
|
\\# Liveness Extra Data: {d} ({})
|
|
\\# Liveness special table: {d} ({})
|
|
\\
|
|
, .{
|
|
fmtIntSizeBin(total_bytes),
|
|
air.instructions.len, fmtIntSizeBin(instruction_bytes),
|
|
air.extra.items.len, fmtIntSizeBin(extra_bytes),
|
|
fmtIntSizeBin(tomb_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
|
|
|
|
var writer: Writer = .{
|
|
.pt = pt,
|
|
.gpa = pt.zcu.gpa,
|
|
.air = air,
|
|
.liveness = liveness,
|
|
.indent = 2,
|
|
.skip_body = false,
|
|
};
|
|
writer.writeBody(stream, air.getMainBody()) catch return;
|
|
}
|
|
|
|
pub fn writeInst(
|
|
air: Air,
|
|
stream: anytype,
|
|
inst: Air.Inst.Index,
|
|
pt: Zcu.PerThread,
|
|
liveness: ?Air.Liveness,
|
|
) void {
|
|
comptime std.debug.assert(build_options.enable_debug_extensions);
|
|
var writer: Writer = .{
|
|
.pt = pt,
|
|
.gpa = pt.zcu.gpa,
|
|
.air = air,
|
|
.liveness = liveness,
|
|
.indent = 2,
|
|
.skip_body = true,
|
|
};
|
|
writer.writeInst(stream, inst) catch return;
|
|
}
|
|
|
|
pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
|
|
air.write(std.io.getStdErr().writer(), pt, liveness);
|
|
}
|
|
|
|
pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
|
|
air.writeInst(std.io.getStdErr().writer(), inst, pt, liveness);
|
|
}
|
|
|
|
const Writer = struct {
|
|
pt: Zcu.PerThread,
|
|
gpa: Allocator,
|
|
air: Air,
|
|
liveness: ?Air.Liveness,
|
|
indent: usize,
|
|
skip_body: bool,
|
|
|
|
fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void {
|
|
for (body) |inst| {
|
|
try w.writeInst(s, inst);
|
|
try s.writeByte('\n');
|
|
}
|
|
}
|
|
|
|
fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)];
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
try s.print("{}{c}= {s}(", .{
|
|
inst,
|
|
@as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
|
|
@tagName(tag),
|
|
});
|
|
switch (tag) {
|
|
.add,
|
|
.add_optimized,
|
|
.add_safe,
|
|
.add_wrap,
|
|
.add_sat,
|
|
.sub,
|
|
.sub_optimized,
|
|
.sub_safe,
|
|
.sub_wrap,
|
|
.sub_sat,
|
|
.mul,
|
|
.mul_optimized,
|
|
.mul_safe,
|
|
.mul_wrap,
|
|
.mul_sat,
|
|
.div_float,
|
|
.div_trunc,
|
|
.div_floor,
|
|
.div_exact,
|
|
.rem,
|
|
.mod,
|
|
.bit_and,
|
|
.bit_or,
|
|
.xor,
|
|
.cmp_lt,
|
|
.cmp_lte,
|
|
.cmp_eq,
|
|
.cmp_gte,
|
|
.cmp_gt,
|
|
.cmp_neq,
|
|
.bool_and,
|
|
.bool_or,
|
|
.store,
|
|
.store_safe,
|
|
.array_elem_val,
|
|
.slice_elem_val,
|
|
.ptr_elem_val,
|
|
.shl,
|
|
.shl_exact,
|
|
.shl_sat,
|
|
.shr,
|
|
.shr_exact,
|
|
.set_union_tag,
|
|
.min,
|
|
.max,
|
|
.div_float_optimized,
|
|
.div_trunc_optimized,
|
|
.div_floor_optimized,
|
|
.div_exact_optimized,
|
|
.rem_optimized,
|
|
.mod_optimized,
|
|
.cmp_lt_optimized,
|
|
.cmp_lte_optimized,
|
|
.cmp_eq_optimized,
|
|
.cmp_gte_optimized,
|
|
.cmp_gt_optimized,
|
|
.cmp_neq_optimized,
|
|
.memcpy,
|
|
.memmove,
|
|
.memset,
|
|
.memset_safe,
|
|
=> try w.writeBinOp(s, inst),
|
|
|
|
.is_null,
|
|
.is_non_null,
|
|
.is_null_ptr,
|
|
.is_non_null_ptr,
|
|
.is_err,
|
|
.is_non_err,
|
|
.is_err_ptr,
|
|
.is_non_err_ptr,
|
|
.ret,
|
|
.ret_safe,
|
|
.ret_load,
|
|
.is_named_enum_value,
|
|
.tag_name,
|
|
.error_name,
|
|
.sqrt,
|
|
.sin,
|
|
.cos,
|
|
.tan,
|
|
.exp,
|
|
.exp2,
|
|
.log,
|
|
.log2,
|
|
.log10,
|
|
.floor,
|
|
.ceil,
|
|
.round,
|
|
.trunc_float,
|
|
.neg,
|
|
.neg_optimized,
|
|
.cmp_lt_errors_len,
|
|
.set_err_return_trace,
|
|
.c_va_end,
|
|
=> try w.writeUnOp(s, inst),
|
|
|
|
.trap,
|
|
.breakpoint,
|
|
.dbg_empty_stmt,
|
|
.unreach,
|
|
.ret_addr,
|
|
.frame_addr,
|
|
.save_err_return_trace_index,
|
|
=> try w.writeNoOp(s, inst),
|
|
|
|
.alloc,
|
|
.ret_ptr,
|
|
.err_return_trace,
|
|
.c_va_start,
|
|
=> try w.writeTy(s, inst),
|
|
|
|
.arg => try w.writeArg(s, inst),
|
|
|
|
.not,
|
|
.bitcast,
|
|
.load,
|
|
.fptrunc,
|
|
.fpext,
|
|
.intcast,
|
|
.intcast_safe,
|
|
.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_from_int,
|
|
.splat,
|
|
.int_from_float,
|
|
.int_from_float_optimized,
|
|
.int_from_float_safe,
|
|
.int_from_float_optimized_safe,
|
|
.get_union_tag,
|
|
.clz,
|
|
.ctz,
|
|
.popcount,
|
|
.byte_swap,
|
|
.bit_reverse,
|
|
.abs,
|
|
.error_set_has_value,
|
|
.addrspace_cast,
|
|
.c_va_arg,
|
|
.c_va_copy,
|
|
=> try w.writeTyOp(s, inst),
|
|
|
|
.block, .dbg_inline_block => try w.writeBlock(s, tag, inst),
|
|
|
|
.loop => try w.writeLoop(s, inst),
|
|
|
|
.slice,
|
|
.slice_elem_ptr,
|
|
.ptr_elem_ptr,
|
|
.ptr_add,
|
|
.ptr_sub,
|
|
.add_with_overflow,
|
|
.sub_with_overflow,
|
|
.mul_with_overflow,
|
|
.shl_with_overflow,
|
|
=> try w.writeTyPlBin(s, inst),
|
|
|
|
.call,
|
|
.call_always_tail,
|
|
.call_never_tail,
|
|
.call_never_inline,
|
|
=> try w.writeCall(s, inst),
|
|
|
|
.dbg_var_ptr,
|
|
.dbg_var_val,
|
|
.dbg_arg_inline,
|
|
=> try w.writeDbgVar(s, inst),
|
|
|
|
.struct_field_ptr => try w.writeStructField(s, inst),
|
|
.struct_field_val => try w.writeStructField(s, inst),
|
|
.inferred_alloc => @panic("TODO"),
|
|
.inferred_alloc_comptime => @panic("TODO"),
|
|
.assembly => try w.writeAssembly(s, inst),
|
|
.dbg_stmt => try w.writeDbgStmt(s, inst),
|
|
|
|
.aggregate_init => try w.writeAggregateInit(s, inst),
|
|
.union_init => try w.writeUnionInit(s, inst),
|
|
.br => try w.writeBr(s, inst),
|
|
.switch_dispatch => try w.writeBr(s, inst),
|
|
.repeat => try w.writeRepeat(s, inst),
|
|
.cond_br => try w.writeCondBr(s, inst),
|
|
.@"try", .try_cold => try w.writeTry(s, inst),
|
|
.try_ptr, .try_ptr_cold => try w.writeTryPtr(s, inst),
|
|
.loop_switch_br, .switch_br => try w.writeSwitchBr(s, inst),
|
|
.cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
|
|
.atomic_load => try w.writeAtomicLoad(s, inst),
|
|
.prefetch => try w.writePrefetch(s, inst),
|
|
.atomic_store_unordered => try w.writeAtomicStore(s, inst, .unordered),
|
|
.atomic_store_monotonic => try w.writeAtomicStore(s, inst, .monotonic),
|
|
.atomic_store_release => try w.writeAtomicStore(s, inst, .release),
|
|
.atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .seq_cst),
|
|
.atomic_rmw => try w.writeAtomicRmw(s, inst),
|
|
.field_parent_ptr => try w.writeFieldParentPtr(s, inst),
|
|
.wasm_memory_size => try w.writeWasmMemorySize(s, inst),
|
|
.wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
|
|
.mul_add => try w.writeMulAdd(s, inst),
|
|
.select => try w.writeSelect(s, inst),
|
|
.shuffle_one => try w.writeShuffleOne(s, inst),
|
|
.shuffle_two => try w.writeShuffleTwo(s, inst),
|
|
.reduce, .reduce_optimized => try w.writeReduce(s, inst),
|
|
.cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst),
|
|
.vector_store_elem => try w.writeVectorStoreElem(s, inst),
|
|
.runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst),
|
|
|
|
.work_item_id,
|
|
.work_group_size,
|
|
.work_group_id,
|
|
=> try w.writeWorkDimension(s, inst),
|
|
}
|
|
try s.writeByte(')');
|
|
}
|
|
|
|
fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const bin_op = w.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
|
|
try w.writeOperand(s, inst, 0, bin_op.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, bin_op.rhs);
|
|
}
|
|
|
|
fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const un_op = w.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
|
|
try w.writeOperand(s, inst, 0, un_op);
|
|
}
|
|
|
|
fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
_ = w;
|
|
_ = inst;
|
|
// no-op, no argument to write
|
|
}
|
|
|
|
fn writeType(w: *Writer, s: anytype, ty: Type) !void {
|
|
return ty.print(s, w.pt);
|
|
}
|
|
|
|
fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty = w.air.instructions.items(.data)[@intFromEnum(inst)].ty;
|
|
try w.writeType(s, ty);
|
|
}
|
|
|
|
fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg;
|
|
try w.writeType(s, arg.ty.toType());
|
|
try s.print(", {d}", .{arg.zir_param_index});
|
|
}
|
|
|
|
fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_op = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
|
|
try w.writeType(s, ty_op.ty.toType());
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, ty_op.operand);
|
|
}
|
|
|
|
fn writeBlock(w: *Writer, s: anytype, tag: Air.Inst.Tag, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
try w.writeType(s, ty_pl.ty.toType());
|
|
const body: []const Air.Inst.Index = @ptrCast(switch (tag) {
|
|
inline .block, .dbg_inline_block => |comptime_tag| body: {
|
|
const extra = w.air.extraData(switch (comptime_tag) {
|
|
.block => Air.Block,
|
|
.dbg_inline_block => Air.DbgInlineBlock,
|
|
else => unreachable,
|
|
}, ty_pl.payload);
|
|
switch (comptime_tag) {
|
|
.block => {},
|
|
.dbg_inline_block => {
|
|
try s.writeAll(", ");
|
|
try w.writeInstRef(s, Air.internedToRef(extra.data.func), false);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
break :body w.air.extra.items[extra.end..][0..extra.data.body_len];
|
|
},
|
|
else => unreachable,
|
|
});
|
|
if (w.skip_body) return s.writeAll(", ...");
|
|
const liveness_block: Air.Liveness.BlockSlices = if (w.liveness) |liveness|
|
|
liveness.getBlock(inst)
|
|
else
|
|
.{ .deaths = &.{} };
|
|
|
|
try s.writeAll(", {\n");
|
|
const old_indent = w.indent;
|
|
w.indent += 2;
|
|
try w.writeBody(s, body);
|
|
w.indent = old_indent;
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
try s.writeAll("}");
|
|
|
|
for (liveness_block.deaths) |operand| {
|
|
try s.print(" {}!", .{operand});
|
|
}
|
|
}
|
|
|
|
fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.Block, ty_pl.payload);
|
|
const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
|
|
|
|
try w.writeType(s, ty_pl.ty.toType());
|
|
if (w.skip_body) return s.writeAll(", ...");
|
|
try s.writeAll(", {\n");
|
|
const old_indent = w.indent;
|
|
w.indent += 2;
|
|
try w.writeBody(s, body);
|
|
w.indent = old_indent;
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
try s.writeAll("}");
|
|
}
|
|
|
|
fn writeAggregateInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const zcu = w.pt.zcu;
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const vector_ty = ty_pl.ty.toType();
|
|
const len = @as(usize, @intCast(vector_ty.arrayLen(zcu)));
|
|
const elements = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[ty_pl.payload..][0..len]));
|
|
|
|
try w.writeType(s, vector_ty);
|
|
try s.writeAll(", [");
|
|
for (elements, 0..) |elem, i| {
|
|
if (i != 0) try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, i, elem);
|
|
}
|
|
try s.writeAll("]");
|
|
}
|
|
|
|
fn writeUnionInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.UnionInit, ty_pl.payload).data;
|
|
|
|
try s.print("{d}, ", .{extra.field_index});
|
|
try w.writeOperand(s, inst, 0, extra.init);
|
|
}
|
|
|
|
fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, extra.struct_operand);
|
|
try s.print(", {d}", .{extra.field_index});
|
|
}
|
|
|
|
fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const data = w.air.instructions.items(.data);
|
|
const ty_pl = data[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
|
|
|
|
const inst_ty = data[@intFromEnum(inst)].ty_pl.ty.toType();
|
|
try w.writeType(s, inst_ty);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, extra.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.rhs);
|
|
}
|
|
|
|
fn writeCmpxchg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, extra.ptr);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.expected_value);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 2, extra.new_value);
|
|
try s.print(", {s}, {s}", .{
|
|
@tagName(extra.successOrder()), @tagName(extra.failureOrder()),
|
|
});
|
|
}
|
|
|
|
fn writeMulAdd(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, extra.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.rhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 2, pl_op.operand);
|
|
}
|
|
|
|
fn writeShuffleOne(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const unwrapped = w.air.unwrapShuffleOne(w.pt.zcu, inst);
|
|
try w.writeType(s, unwrapped.result_ty);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, unwrapped.operand);
|
|
try s.writeAll(", [");
|
|
for (unwrapped.mask, 0..) |mask_elem, mask_idx| {
|
|
if (mask_idx > 0) try s.writeAll(", ");
|
|
switch (mask_elem.unwrap()) {
|
|
.elem => |idx| try s.print("elem {d}", .{idx}),
|
|
.value => |val| try s.print("val {}", .{Value.fromInterned(val).fmtValue(w.pt)}),
|
|
}
|
|
}
|
|
try s.writeByte(']');
|
|
}
|
|
|
|
fn writeShuffleTwo(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst);
|
|
try w.writeType(s, unwrapped.result_ty);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, unwrapped.operand_a);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, unwrapped.operand_b);
|
|
try s.writeAll(", [");
|
|
for (unwrapped.mask, 0..) |mask_elem, mask_idx| {
|
|
if (mask_idx > 0) try s.writeAll(", ");
|
|
switch (mask_elem.unwrap()) {
|
|
.a_elem => |idx| try s.print("a_elem {d}", .{idx}),
|
|
.b_elem => |idx| try s.print("b_elem {d}", .{idx}),
|
|
.undef => try s.writeAll("undef"),
|
|
}
|
|
}
|
|
try s.writeByte(']');
|
|
}
|
|
|
|
fn writeSelect(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const zcu = w.pt.zcu;
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
|
|
|
|
const elem_ty = w.typeOfIndex(inst).childType(zcu);
|
|
try w.writeType(s, elem_ty);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 2, extra.rhs);
|
|
}
|
|
|
|
fn writeReduce(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const reduce = w.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
|
|
|
|
try w.writeOperand(s, inst, 0, reduce.operand);
|
|
try s.print(", {s}", .{@tagName(reduce.operation)});
|
|
}
|
|
|
|
fn writeCmpVector(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.VectorCmp, ty_pl.payload).data;
|
|
|
|
try s.print("{s}, ", .{@tagName(extra.compareOperator())});
|
|
try w.writeOperand(s, inst, 0, extra.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.rhs);
|
|
}
|
|
|
|
fn writeVectorStoreElem(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const data = w.air.instructions.items(.data)[@intFromEnum(inst)].vector_store_elem;
|
|
const extra = w.air.extraData(Air.VectorCmp, data.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, data.vector_ptr);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 2, extra.rhs);
|
|
}
|
|
|
|
fn writeRuntimeNavPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ip = &w.pt.zcu.intern_pool;
|
|
const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
|
|
try w.writeType(s, .fromInterned(ty_nav.ty));
|
|
try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)});
|
|
}
|
|
|
|
fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const atomic_load = w.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
|
|
|
|
try w.writeOperand(s, inst, 0, atomic_load.ptr);
|
|
try s.print(", {s}", .{@tagName(atomic_load.order)});
|
|
}
|
|
|
|
fn writePrefetch(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const prefetch = w.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
|
|
|
|
try w.writeOperand(s, inst, 0, prefetch.ptr);
|
|
try s.print(", {s}, {d}, {s}", .{
|
|
@tagName(prefetch.rw), prefetch.locality, @tagName(prefetch.cache),
|
|
});
|
|
}
|
|
|
|
fn writeAtomicStore(
|
|
w: *Writer,
|
|
s: anytype,
|
|
inst: Air.Inst.Index,
|
|
order: std.builtin.AtomicOrder,
|
|
) @TypeOf(s).Error!void {
|
|
const bin_op = w.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
|
|
try w.writeOperand(s, inst, 0, bin_op.lhs);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, bin_op.rhs);
|
|
try s.print(", {s}", .{@tagName(order)});
|
|
}
|
|
|
|
fn writeAtomicRmw(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1, extra.operand);
|
|
try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
|
|
}
|
|
|
|
fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
|
|
|
|
try w.writeOperand(s, inst, 0, extra.field_ptr);
|
|
try s.print(", {d}", .{extra.field_index});
|
|
}
|
|
|
|
fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.Asm, ty_pl.payload);
|
|
const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
|
|
const clobbers_len = @as(u31, @truncate(extra.data.flags));
|
|
var extra_i: usize = extra.end;
|
|
var op_index: usize = 0;
|
|
|
|
const ret_ty = w.typeOfIndex(inst);
|
|
try w.writeType(s, ret_ty);
|
|
|
|
if (is_volatile) {
|
|
try s.writeAll(", volatile");
|
|
}
|
|
|
|
const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.outputs_len]));
|
|
extra_i += outputs.len;
|
|
const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len]));
|
|
extra_i += inputs.len;
|
|
|
|
for (outputs) |output| {
|
|
const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]);
|
|
const constraint = std.mem.sliceTo(extra_bytes, 0);
|
|
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
|
|
|
|
// This equation accounts for the fact that even if we have exactly 4 bytes
|
|
// for the strings and their null terminators, we still use the next u32
|
|
// for the null terminator.
|
|
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
|
|
|
|
if (output == .none) {
|
|
try s.print(", [{s}] -> {s}", .{ name, constraint });
|
|
} else {
|
|
try s.print(", [{s}] out {s} = (", .{ name, constraint });
|
|
try w.writeOperand(s, inst, op_index, output);
|
|
op_index += 1;
|
|
try s.writeByte(')');
|
|
}
|
|
}
|
|
|
|
for (inputs) |input| {
|
|
const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]);
|
|
const constraint = std.mem.sliceTo(extra_bytes, 0);
|
|
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
|
|
// This equation accounts for the fact that even if we have exactly 4 bytes
|
|
// for the strings and their null terminators, we still use the next u32
|
|
// for the null terminator.
|
|
extra_i += (constraint.len + name.len + 1) / 4 + 1;
|
|
|
|
try s.print(", [{s}] in {s} = (", .{ name, constraint });
|
|
try w.writeOperand(s, inst, op_index, input);
|
|
op_index += 1;
|
|
try s.writeByte(')');
|
|
}
|
|
|
|
{
|
|
var clobber_i: u32 = 0;
|
|
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
|
const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]);
|
|
const clobber = std.mem.sliceTo(extra_bytes, 0);
|
|
// This equation accounts for the fact that even if we have exactly 4 bytes
|
|
// for the string, we still use the next u32 for the null terminator.
|
|
extra_i += clobber.len / 4 + 1;
|
|
|
|
try s.writeAll(", ~{");
|
|
try s.writeAll(clobber);
|
|
try s.writeAll("}");
|
|
}
|
|
}
|
|
const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len];
|
|
try s.print(", \"{}\"", .{std.zig.fmtEscapes(asm_source)});
|
|
}
|
|
|
|
fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const dbg_stmt = w.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
|
|
try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
|
|
}
|
|
|
|
fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
|
|
try s.print(", \"{}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))});
|
|
}
|
|
|
|
fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.Call, pl_op.payload);
|
|
const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len]));
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
try s.writeAll(", [");
|
|
for (args, 0..) |arg, i| {
|
|
if (i != 0) try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 1 + i, arg);
|
|
}
|
|
try s.writeAll("]");
|
|
}
|
|
|
|
fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const br = w.air.instructions.items(.data)[@intFromEnum(inst)].br;
|
|
try w.writeInstIndex(s, br.block_inst, false);
|
|
try s.writeAll(", ");
|
|
try w.writeOperand(s, inst, 0, br.operand);
|
|
}
|
|
|
|
fn writeRepeat(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const repeat = w.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
|
|
try w.writeInstIndex(s, repeat.loop_inst, false);
|
|
}
|
|
|
|
fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.Try, pl_op.payload);
|
|
const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
|
|
const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
|
|
liveness.getCondBr(inst)
|
|
else
|
|
.{ .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("{}!", .{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(" {}!", .{operand});
|
|
}
|
|
}
|
|
|
|
fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
|
const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
|
|
const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
|
|
const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
|
|
liveness.getCondBr(inst)
|
|
else
|
|
.{ .then_deaths = &.{}, .else_deaths = &.{} };
|
|
|
|
try w.writeOperand(s, inst, 0, extra.data.ptr);
|
|
|
|
try s.writeAll(", ");
|
|
try w.writeType(s, ty_pl.ty.toType());
|
|
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("{}!", .{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(" {}!", .{operand});
|
|
}
|
|
}
|
|
|
|
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
const extra = w.air.extraData(Air.CondBr, pl_op.payload);
|
|
const then_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.then_body_len]);
|
|
const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
|
|
const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
|
|
liveness.getCondBr(inst)
|
|
else
|
|
.{ .then_deaths = &.{}, .else_deaths = &.{} };
|
|
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
if (w.skip_body) return s.writeAll(", ...");
|
|
try s.writeAll(",");
|
|
if (extra.data.branch_hints.true != .none) {
|
|
try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)});
|
|
}
|
|
if (extra.data.branch_hints.then_cov != .none) {
|
|
try s.print(" {s}", .{@tagName(extra.data.branch_hints.then_cov)});
|
|
}
|
|
try s.writeAll(" {\n");
|
|
const old_indent = w.indent;
|
|
w.indent += 2;
|
|
|
|
if (liveness_condbr.then_deaths.len != 0) {
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
for (liveness_condbr.then_deaths, 0..) |operand, i| {
|
|
if (i != 0) try s.writeAll(" ");
|
|
try s.print("{}!", .{operand});
|
|
}
|
|
try s.writeAll("\n");
|
|
}
|
|
|
|
try w.writeBody(s, then_body);
|
|
try s.writeByteNTimes(' ', old_indent);
|
|
try s.writeAll("},");
|
|
if (extra.data.branch_hints.false != .none) {
|
|
try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)});
|
|
}
|
|
if (extra.data.branch_hints.else_cov != .none) {
|
|
try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)});
|
|
}
|
|
try s.writeAll(" {\n");
|
|
|
|
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("{}!", .{operand});
|
|
}
|
|
try s.writeAll("\n");
|
|
}
|
|
|
|
try w.writeBody(s, else_body);
|
|
w.indent = old_indent;
|
|
|
|
try s.writeByteNTimes(' ', old_indent);
|
|
try s.writeAll("}");
|
|
}
|
|
|
|
fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const switch_br = w.air.unwrapSwitch(inst);
|
|
|
|
const liveness: Air.Liveness.SwitchBrTable = if (w.liveness) |liveness|
|
|
liveness.getSwitchBr(w.gpa, inst, switch_br.cases_len + 1) catch
|
|
@panic("out of memory")
|
|
else blk: {
|
|
const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.cases_len + 1) catch
|
|
@panic("out of memory");
|
|
@memset(slice, &.{});
|
|
break :blk .{ .deaths = slice };
|
|
};
|
|
defer w.gpa.free(liveness.deaths);
|
|
|
|
try w.writeOperand(s, inst, 0, switch_br.operand);
|
|
if (w.skip_body) return s.writeAll(", ...");
|
|
const old_indent = w.indent;
|
|
w.indent += 2;
|
|
|
|
var it = switch_br.iterateCases();
|
|
while (it.next()) |case| {
|
|
try s.writeAll(", [");
|
|
for (case.items, 0..) |item, item_i| {
|
|
if (item_i != 0) try s.writeAll(", ");
|
|
try w.writeInstRef(s, item, false);
|
|
}
|
|
for (case.ranges, 0..) |range, range_i| {
|
|
if (range_i != 0 or case.items.len != 0) try s.writeAll(", ");
|
|
try w.writeInstRef(s, range[0], false);
|
|
try s.writeAll("...");
|
|
try w.writeInstRef(s, range[1], false);
|
|
}
|
|
try s.writeAll("] ");
|
|
const hint = switch_br.getHint(case.idx);
|
|
if (hint != .none) {
|
|
try s.print(".{s} ", .{@tagName(hint)});
|
|
}
|
|
try s.writeAll("=> {\n");
|
|
w.indent += 2;
|
|
|
|
const deaths = liveness.deaths[case.idx];
|
|
if (deaths.len != 0) {
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
for (deaths, 0..) |operand, i| {
|
|
if (i != 0) try s.writeAll(" ");
|
|
try s.print("{}!", .{operand});
|
|
}
|
|
try s.writeAll("\n");
|
|
}
|
|
|
|
try w.writeBody(s, case.body);
|
|
w.indent -= 2;
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
try s.writeAll("}");
|
|
}
|
|
|
|
const else_body = it.elseBody();
|
|
if (else_body.len != 0) {
|
|
try s.writeAll(", else ");
|
|
const hint = switch_br.getElseHint();
|
|
if (hint != .none) {
|
|
try s.print(".{s} ", .{@tagName(hint)});
|
|
}
|
|
try s.writeAll("=> {\n");
|
|
w.indent += 2;
|
|
|
|
const deaths = liveness.deaths[liveness.deaths.len - 1];
|
|
if (deaths.len != 0) {
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
for (deaths, 0..) |operand, i| {
|
|
if (i != 0) try s.writeAll(" ");
|
|
try s.print("{}!", .{operand});
|
|
}
|
|
try s.writeAll("\n");
|
|
}
|
|
|
|
try w.writeBody(s, else_body);
|
|
w.indent -= 2;
|
|
try s.writeByteNTimes(' ', w.indent);
|
|
try s.writeAll("}");
|
|
}
|
|
|
|
try s.writeAll("\n");
|
|
try s.writeByteNTimes(' ', old_indent);
|
|
}
|
|
|
|
fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
try s.print("{d}", .{pl_op.payload});
|
|
}
|
|
|
|
fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
try s.print("{d}, ", .{pl_op.payload});
|
|
try w.writeOperand(s, inst, 0, pl_op.operand);
|
|
}
|
|
|
|
fn writeWorkDimension(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
|
const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
|
|
try s.print("{d}", .{pl_op.payload});
|
|
}
|
|
|
|
fn writeOperand(
|
|
w: *Writer,
|
|
s: anytype,
|
|
inst: Air.Inst.Index,
|
|
op_index: usize,
|
|
operand: Air.Inst.Ref,
|
|
) @TypeOf(s).Error!void {
|
|
const small_tomb_bits = Air.Liveness.bpi - 1;
|
|
const dies = if (w.liveness) |liveness| blk: {
|
|
if (op_index < small_tomb_bits)
|
|
break :blk liveness.operandDies(inst, @intCast(op_index));
|
|
var extra_index = liveness.special.get(inst).?;
|
|
var tomb_op_index: usize = small_tomb_bits;
|
|
while (true) {
|
|
const bits = liveness.extra[extra_index];
|
|
if (op_index < tomb_op_index + 31) {
|
|
break :blk @as(u1, @truncate(bits >> @as(u5, @intCast(op_index - tomb_op_index)))) != 0;
|
|
}
|
|
if ((bits >> 31) != 0) break :blk false;
|
|
extra_index += 1;
|
|
tomb_op_index += 31;
|
|
}
|
|
} else false;
|
|
return w.writeInstRef(s, operand, dies);
|
|
}
|
|
|
|
fn writeInstRef(
|
|
w: *Writer,
|
|
s: anytype,
|
|
operand: Air.Inst.Ref,
|
|
dies: bool,
|
|
) @TypeOf(s).Error!void {
|
|
if (@intFromEnum(operand) < InternPool.static_len) {
|
|
return s.print("@{}", .{operand});
|
|
} else if (operand.toInterned()) |ip_index| {
|
|
const pt = w.pt;
|
|
const ty = Type.fromInterned(pt.zcu.intern_pool.indexToKey(ip_index).typeOf());
|
|
try s.print("<{}, {}>", .{
|
|
ty.fmt(pt),
|
|
Value.fromInterned(ip_index).fmtValue(pt),
|
|
});
|
|
} else {
|
|
return w.writeInstIndex(s, operand.toIndex().?, dies);
|
|
}
|
|
}
|
|
|
|
fn writeInstIndex(
|
|
w: *Writer,
|
|
s: anytype,
|
|
inst: Air.Inst.Index,
|
|
dies: bool,
|
|
) @TypeOf(s).Error!void {
|
|
_ = w;
|
|
try s.print("{}", .{inst});
|
|
if (dies) try s.writeByte('!');
|
|
}
|
|
|
|
fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type {
|
|
const zcu = w.pt.zcu;
|
|
return w.air.typeOfIndex(inst, &zcu.intern_pool);
|
|
}
|
|
};
|