x86_64: implement union access

This commit is contained in:
Jacob Young 2025-01-08 06:38:47 -05:00
parent 5069f574f4
commit 63730441d0
4 changed files with 286 additions and 71 deletions

View file

@ -23,6 +23,7 @@ pub const Coverage = @import("debug/Coverage.zig");
pub const FormattedPanic = @import("debug/FormattedPanic.zig");
pub const SimplePanic = @import("debug/SimplePanic.zig");
pub const NoPanic = @import("debug/NoPanic.zig");
/// Unresolved source locations can be represented with a single `usize` that
/// corresponds to a virtual memory address of the program counter. Combined

59
lib/std/debug/NoPanic.zig Normal file
View file

@ -0,0 +1,59 @@
//! This namespace can be used with `pub const Panic = std.debug.NoPanic;` in the root file.
//! It emits as little code as possible, for testing purposes.
//!
//! For a functional alternative, see `std.debug.FormattedPanic`.
const std = @import("../std.zig");
pub fn call(_: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn sentinelMismatch(_: anytype, _: anytype) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn unwrapError(_: ?*std.builtin.StackTrace, _: anyerror) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn outOfBounds(_: usize, _: usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn startGreaterThanEnd(_: usize, _: usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn inactiveUnionField(_: anytype, _: anytype) noreturn {
@branchHint(.cold);
@trap();
}
pub const messages = struct {
pub const reached_unreachable = "";
pub const unwrap_null = "";
pub const cast_to_null = "";
pub const incorrect_alignment = "";
pub const invalid_error_code = "";
pub const cast_truncated_data = "";
pub const negative_to_unsigned = "";
pub const integer_overflow = "";
pub const shl_overflow = "";
pub const shr_overflow = "";
pub const divide_by_zero = "";
pub const exact_division_remainder = "";
pub const integer_part_out_of_bounds = "";
pub const corrupt_switch = "";
pub const shift_rhs_too_big = "";
pub const invalid_enum_value = "";
pub const for_len_mismatch = "";
pub const memcpy_len_mismatch = "";
pub const memcpy_alias = "";
pub const noreturn_returned = "";
};

View file

@ -2467,8 +2467,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.memcpy => try cg.airMemcpy(inst),
.memset => try cg.airMemset(inst, false),
.memset_safe => try cg.airMemset(inst, true),
.set_union_tag => try cg.airSetUnionTag(inst),
.get_union_tag => try cg.airGetUnionTag(inst),
.ctz => try cg.airCtz(inst),
.popcount => try cg.airPopCount(inst),
.byte_swap => try cg.airByteSwap(inst),
@ -2480,7 +2478,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.shuffle => try cg.airShuffle(inst),
.reduce => try cg.airReduce(inst),
.aggregate_init => try cg.airAggregateInit(inst),
.union_init => try cg.airUnionInit(inst),
.prefetch => try cg.airPrefetch(inst),
.mul_add => try cg.airMulAdd(inst),
@ -2528,7 +2525,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, comptime &.{ .{
cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
.patterns = &.{
.{ .src = .{ .to_gpr, .simm32 } },
},
@ -2645,7 +2642,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, comptime &.{ .{
cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{
.patterns = &.{
.{ .src = .{ .to_gpr, .simm32 } },
},
@ -2772,20 +2769,22 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
try res[0].moveTo(inst, cg);
},
.alloc => if (use_old) try cg.airAlloc(inst) else {
var slot = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .lea_frame = .{
const ty = air_datas[@intFromEnum(inst)].ty;
var slot = try cg.tempInit(ty, .{ .lea_frame = .{
.index = try cg.allocMemPtr(inst),
} });
try slot.moveTo(inst, cg);
},
.inferred_alloc, .inferred_alloc_comptime => unreachable,
.ret_ptr => if (use_old) try cg.airRetPtr(inst) else {
const ty = air_datas[@intFromEnum(inst)].ty;
var slot = switch (cg.ret_mcv.long) {
else => unreachable,
.none => try cg.tempFromValue(cg.typeOfIndex(inst), .{ .lea_frame = .{
.none => try cg.tempInit(ty, .{ .lea_frame = .{
.index = try cg.allocMemPtr(inst),
} }),
.load_frame => slot: {
var slot = try cg.tempFromValue(cg.typeOfIndex(inst), cg.ret_mcv.long);
var slot = try cg.tempInit(ty, cg.ret_mcv.long);
try slot.toOffset(cg.ret_mcv.short.indirect.off, cg);
break :slot slot;
},
@ -2797,7 +2796,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const bin_op = air_datas[@intFromEnum(inst)].bin_op;
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, switch (@as(Mir.Inst.Tag, switch (air_tag) {
cg.select(&res, &.{cg.typeOf(bin_op.lhs)}, &ops, switch (@as(Mir.Inst.Tag, switch (air_tag) {
else => unreachable,
.bit_and => .@"and",
.bit_or => .@"or",
@ -3156,7 +3155,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const ty_op = air_datas[@intFromEnum(inst)].ty_op;
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, comptime &.{ .{
cg.select(&res, &.{ty_op.ty.toType()}, &ops, comptime &.{ .{
.src_constraints = .{ .{ .signed_or_exact_int = .byte }, .any },
.patterns = &.{
.{ .src = .{ .mut_mem, .none } },
@ -4239,14 +4238,14 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.trap => try cg.asmOpOnly(.{ ._, .ud2 }),
.breakpoint => try cg.asmOpOnly(.{ ._, .int3 }),
.ret_addr => if (use_old) try cg.airRetAddr(inst) else {
var slot = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .load_frame = .{
var slot = try cg.tempInit(.usize, .{ .load_frame = .{
.index = .ret_addr,
} });
while (try slot.toRegClass(true, .general_purpose, cg)) {}
try slot.moveTo(inst, cg);
},
.frame_addr => if (use_old) try cg.airFrameAddress(inst) else {
var slot = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .lea_frame = .{
var slot = try cg.tempInit(.usize, .{ .lea_frame = .{
.index = .base_ptr,
} });
try slot.moveTo(inst, cg);
@ -4260,7 +4259,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const ty_op = air_datas[@intFromEnum(inst)].ty_op;
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, comptime &.{ .{
cg.select(&res, &.{ty_op.ty.toType()}, &ops, comptime &.{ .{
.required_features = .{ .slow_incdec, null, null, null },
.src_constraints = .{ .{ .exact_signed_int = 1 }, .any },
.patterns = &.{
@ -6997,7 +6996,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
switch (extra.compareOperator()) {
.lt => unreachable,
.lte => unreachable,
.eq, .neq => |cmp_op| cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, switch (@as(Condition, switch (cmp_op) {
.eq, .neq => |cmp_op| cg.select(&res, &.{ty_pl.ty.toType()}, &ops, switch (@as(Condition, switch (cmp_op) {
else => unreachable,
.eq => .e,
.neq => .ne,
@ -8825,7 +8824,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unsigned;
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, switch (@as(Condition, switch (signedness) {
cg.select(&res, &.{.bool}, &ops, switch (@as(Condition, switch (signedness) {
.signed => switch (air_tag) {
else => unreachable,
.cmp_lt, .cmp_lt_optimized => .l,
@ -9011,7 +9010,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
});
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
var res: [1]Temp = undefined;
cg.select(&res, &.{cg.typeOfIndex(inst)}, &ops, switch (@as(Condition, switch (air_tag) {
cg.select(&res, &.{.bool}, &ops, switch (@as(Condition, switch (air_tag) {
else => unreachable,
.cmp_eq, .cmp_eq_optimized => .e,
.cmp_neq, .cmp_neq_optimized => .ne,
@ -9540,7 +9539,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.fromSize(opt_child_abi_size) }),
.u(0),
);
var is_null = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .eflags = .e });
var is_null = try cg.tempInit(.bool, .{ .eflags = .e });
try ops[0].die(cg);
try is_null.moveTo(inst, cg);
},
@ -9563,7 +9562,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.fromSize(opt_child_abi_size) }),
.u(0),
);
var is_non_null = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .eflags = .ne });
var is_non_null = try cg.tempInit(.bool, .{ .eflags = .ne });
try ops[0].die(cg);
try is_non_null.moveTo(inst, cg);
},
@ -9581,7 +9580,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(eu_err_ty) }),
.u(0),
);
var is_err = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .eflags = .ne });
var is_err = try cg.tempInit(.bool, .{ .eflags = .ne });
try ops[0].die(cg);
try is_err.moveTo(inst, cg);
},
@ -9599,7 +9598,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(eu_err_ty) }),
.u(0),
);
var is_non_err = try cg.tempFromValue(cg.typeOfIndex(inst), .{ .eflags = .e });
var is_non_err = try cg.tempInit(.bool, .{ .eflags = .e });
try ops[0].die(cg);
try is_non_err.moveTo(inst, cg);
},
@ -9631,8 +9630,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const opt_child_ty = opt_ty.optionalChild(zcu);
const opt_child_abi_size: i32 = @intCast(opt_child_ty.abiSize(zcu));
try ops[0].toOffset(opt_child_abi_size, cg);
var has_value = try cg.tempFromValue(.bool, .{ .immediate = 1 });
try ops[0].store(&has_value, cg);
var has_value = try cg.tempInit(.bool, .{ .immediate = 1 });
try ops[0].store(0, &has_value, cg);
try has_value.die(cg);
try ops[0].toOffset(-opt_child_abi_size, cg);
}
@ -9654,7 +9653,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const eu_err_off: i32 = @intCast(codegen.errUnionErrorOffset(eu_pl_ty, zcu));
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(eu_err_off, cg);
var err = try ops[0].load(eu_ty.errorUnionSet(zcu), cg);
var err = try ops[0].load(0, eu_ty.errorUnionSet(zcu), cg);
try ops[0].die(cg);
try err.moveTo(inst, cg);
},
@ -9667,8 +9666,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const eu_pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(eu_pl_ty, zcu));
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(eu_err_off, cg);
var no_err = try cg.tempFromValue(eu_err_ty, .{ .immediate = 0 });
try ops[0].store(&no_err, cg);
var no_err = try cg.tempInit(eu_err_ty, .{ .immediate = 0 });
try ops[0].store(0, &no_err, cg);
try no_err.die(cg);
try ops[0].toOffset(eu_pl_off - eu_err_off, cg);
try ops[0].moveTo(inst, cg);
@ -9679,7 +9678,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{extra.struct_operand});
try ops[0].toOffset(cg.fieldOffset(
cg.typeOf(extra.struct_operand),
cg.typeOfIndex(inst),
ty_pl.ty.toType(),
extra.field_index,
), cg);
try ops[0].moveTo(inst, cg);
@ -9689,7 +9688,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(cg.fieldOffset(
cg.typeOf(ty_op.operand),
cg.typeOfIndex(inst),
ty_op.ty.toType(),
0,
), cg);
try ops[0].moveTo(inst, cg);
@ -9699,7 +9698,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(cg.fieldOffset(
cg.typeOf(ty_op.operand),
cg.typeOfIndex(inst),
ty_op.ty.toType(),
1,
), cg);
try ops[0].moveTo(inst, cg);
@ -9709,7 +9708,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(cg.fieldOffset(
cg.typeOf(ty_op.operand),
cg.typeOfIndex(inst),
ty_op.ty.toType(),
2,
), cg);
try ops[0].moveTo(inst, cg);
@ -9719,11 +9718,29 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
try ops[0].toOffset(cg.fieldOffset(
cg.typeOf(ty_op.operand),
cg.typeOfIndex(inst),
ty_op.ty.toType(),
3,
), cg);
try ops[0].moveTo(inst, cg);
},
.set_union_tag => if (use_old) try cg.airSetUnionTag(inst) else {
const bin_op = air_datas[@intFromEnum(inst)].bin_op;
const union_ty = cg.typeOf(bin_op.lhs).childType(zcu);
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
const union_layout = union_ty.unionGetLayout(zcu);
if (union_layout.tag_size > 0) try ops[0].store(@intCast(union_layout.tagOffset()), &ops[1], cg);
for (ops) |op| try op.die(cg);
},
.get_union_tag => if (use_old) try cg.airGetUnionTag(inst) else {
const ty_op = air_datas[@intFromEnum(inst)].ty_op;
const union_ty = cg.typeOf(ty_op.operand);
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
const union_layout = union_ty.unionGetLayout(zcu);
assert(union_layout.tag_size > 0);
var res = try ops[0].read(@intCast(union_layout.tagOffset()), ty_op.ty.toType(), cg);
for (ops) |op| if (op.index != res.index) try op.die(cg);
try res.moveTo(inst, cg);
},
.slice => if (use_old) try cg.airSlice(inst) else {
const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
@ -9764,7 +9781,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
var res: [1]Temp = undefined;
const res_ty = cg.typeOfIndex(inst);
const res_ty = cg.typeOf(bin_op.lhs).elemType2(zcu);
cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{
.dst_constraints = .{.{ .int = .byte }},
.patterns = &.{
@ -9840,7 +9857,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
} },
} }) catch |err| switch (err) {
error.SelectFailed => switch (res_ty.abiSize(zcu)) {
0 => res[0] = try cg.tempFromValue(res_ty, .none),
0 => res[0] = try cg.tempInit(res_ty, .none),
else => |elem_size| {
while (true) for (&ops) |*op| {
if (try op.toRegClass(true, .general_purpose, cg)) break;
@ -9878,7 +9895,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.scale = .fromFactor(@intCast(elem_size)),
} },
});
res[0] = try ops[0].load(res_ty, cg);
res[0] = try ops[0].load(0, res_ty, cg);
},
},
else => |e| return e,
@ -9897,7 +9914,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs });
try ops[0].toSlicePtr(cg);
const dst_ty = cg.typeOfIndex(inst);
const dst_ty = ty_pl.ty.toType();
if (dst_ty.ptrInfo(zcu).flags.vector_index == .none) zero_offset: {
const elem_size = dst_ty.childType(zcu).abiSize(zcu);
if (elem_size == 0) break :zero_offset;
@ -9944,19 +9961,38 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.array_to_slice => if (use_old) try cg.airArrayToSlice(inst) else {
const ty_op = air_datas[@intFromEnum(inst)].ty_op;
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
var len = try cg.tempFromValue(.usize, .{
var len = try cg.tempInit(.usize, .{
.immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu),
});
try ops[0].toPair(&len, cg);
try ops[0].moveTo(inst, cg);
},
.error_set_has_value => return cg.fail("TODO implement error_set_has_value", .{}),
.union_init => if (use_old) try cg.airUnionInit(inst) else {
const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data;
const union_ty = ty_pl.ty.toType();
var ops = try cg.tempsFromOperands(inst, .{extra.init});
var res = try cg.tempAllocMem(union_ty);
const union_layout = union_ty.unionGetLayout(zcu);
if (union_layout.tag_size > 0) {
var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex(
union_ty.unionTagTypeSafety(zcu).?,
extra.field_index,
));
try res.write(@intCast(union_layout.tagOffset()), &tag_temp, cg);
try tag_temp.die(cg);
}
try res.write(@intCast(union_layout.payloadOffset()), &ops[0], cg);
try ops[0].die(cg);
try res.moveTo(inst, cg);
},
.field_parent_ptr => if (use_old) try cg.airFieldParentPtr(inst) else {
const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
var ops = try cg.tempsFromOperands(inst, .{extra.field_ptr});
try ops[0].toOffset(-cg.fieldOffset(
cg.typeOfIndex(inst),
ty_pl.ty.toType(),
cg.typeOf(extra.field_ptr),
extra.field_index,
), cg);
@ -10273,7 +10309,7 @@ fn allocRegOrMemAdvanced(self: *CodeGen, ty: Type, inst: ?Air.Inst.Index, reg_ok
};
if (reg_ok) need_mem: {
if (abi_size <= @as(u32, switch (ty.zigTypeTag(zcu)) {
if (std.math.isPowerOfTwo(abi_size) and abi_size <= @as(u32, switch (ty.zigTypeTag(zcu)) {
.float => switch (ty.floatBits(self.target.*)) {
16, 32, 64, 128 => 16,
80 => break :need_mem,
@ -11042,7 +11078,8 @@ fn airTrunc(self: *CodeGen, inst: Air.Inst.Index) !void {
if (src_mcv.getReg()) |reg| self.register_manager.lockRegAssumeUnused(reg) else null;
defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
const dst_mcv = if (src_mcv.isRegister() and src_mcv.getReg().?.class() == self.regClassForType(dst_ty) and
self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
src_mcv
else if (dst_abi_size <= 8)
try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv)
@ -15513,12 +15550,12 @@ fn airLoad(self: *CodeGen, inst: Air.Inst.Index) !void {
const ptr_ty = self.typeOf(ty_op.operand);
const elem_size = elem_ty.abiSize(zcu);
const elem_rc = self.regSetForType(elem_ty);
const ptr_rc = self.regSetForType(ptr_ty);
const elem_rs = self.regSetForType(elem_ty);
const ptr_rs = self.regSetForType(ptr_ty);
const ptr_mcv = try self.resolveInst(ty_op.operand);
const dst_mcv = if (elem_size <= 8 and elem_rc.supersetOf(ptr_rc) and
self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv))
const dst_mcv = if (elem_size <= 8 and std.math.isPowerOfTwo(elem_size) and
elem_rs.supersetOf(ptr_rs) and self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv))
// The MCValue that holds the pointer can be re-used as the value.
ptr_mcv
else
@ -28295,17 +28332,19 @@ const Temp = struct {
return true;
}
fn load(ptr: *Temp, val_ty: Type, cg: *CodeGen) !Temp {
fn load(ptr: *Temp, disp: i32, val_ty: Type, cg: *CodeGen) !Temp {
const val = try cg.tempAlloc(val_ty);
switch (val.tracking(cg).short) {
const val_mcv = val.tracking(cg).short;
switch (val_mcv) {
else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.register => |val_reg| {
while (try ptr.toLea(cg)) {}
try cg.genSetReg(val_reg, val_ty, ptr.tracking(cg).short.deref(), .{});
try cg.genSetReg(val_reg, val_ty, ptr.tracking(cg).short.offset(disp).deref(), .{});
},
.load_frame => |val_frame_addr| {
var val_ptr = try cg.tempFromValue(.usize, .{ .lea_frame = val_frame_addr });
var len = try cg.tempFromValue(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) });
.memory, .indirect, .load_frame, .load_symbol => {
try ptr.toOffset(disp, cg);
var val_ptr = try cg.tempInit(.usize, val_mcv.address());
var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) });
try val_ptr.memcpy(ptr, &len, cg);
try val_ptr.die(cg);
try len.die(cg);
@ -28314,27 +28353,38 @@ const Temp = struct {
return val;
}
fn store(ptr: *Temp, val: *Temp, cg: *CodeGen) !void {
fn store(ptr: *Temp, disp: i32, val: *Temp, cg: *CodeGen) !void {
const val_ty = val.typeOf(cg);
const val_abi_size: u32 = @intCast(val_ty.abiSize(cg.pt.zcu));
val: switch (val.tracking(cg).short) {
else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.immediate => |imm| if (std.math.cast(i32, imm)) |s| {
.immediate => |val_imm| {
const val_op: Immediate = if (std.math.cast(u32, val_imm)) |val_uimm32|
.u(val_uimm32)
else if (std.math.cast(i32, @as(i64, @bitCast(val_imm)))) |val_simm32|
.s(val_simm32)
else
continue :val .{ .register = undefined };
while (try ptr.toLea(cg)) {}
try cg.asmMemoryImmediate(
.{ ._, .mov },
try ptr.tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(val_ty) }),
.s(s),
try ptr.tracking(cg).short.deref().mem(cg, .{
.size = cg.memSize(val_ty),
.disp = disp,
}),
val_op,
);
} else continue :val .{ .register = undefined },
},
.register => {
while (try ptr.toLea(cg) or try val.toRegClass(true, .general_purpose, cg)) {}
const val_reg = val.tracking(cg).short.register;
switch (val_reg.class()) {
.general_purpose => try cg.asmMemoryRegister(
.{ ._, .mov },
try ptr.tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(val_ty) }),
registerAlias(val_reg, val_abi_size),
try ptr.tracking(cg).short.deref().mem(cg, .{
.size = cg.memSize(val_ty),
.disp = disp,
}),
registerAlias(val_reg, @intCast(val_ty.abiSize(cg.pt.zcu))),
),
else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
}
@ -28342,6 +28392,111 @@ const Temp = struct {
}
}
fn read(src: *Temp, disp: i32, val_ty: Type, cg: *CodeGen) !Temp {
const val = try cg.tempAlloc(val_ty);
while (try src.toBase(cg)) {}
const val_mcv = val.tracking(cg).short;
switch (val_mcv) {
else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.register => |val_reg| try src.readReg(disp, val_ty, registerAlias(
val_reg,
@intCast(val_ty.abiSize(cg.pt.zcu)),
), cg),
}
return val;
}
fn readReg(src: Temp, disp: i32, dst_ty: Type, dst_reg: Register, cg: *CodeGen) !void {
const strat = try cg.moveStrategy(dst_ty, dst_reg.class(), false);
try strat.read(cg, dst_reg, try src.tracking(cg).short.mem(cg, .{
.size = .fromBitSize(@min(8 * dst_ty.abiSize(cg.pt.zcu), dst_reg.bitSize())),
.disp = disp,
}));
}
fn write(dst: *Temp, disp: i32, val: *Temp, cg: *CodeGen) !void {
const val_ty = val.typeOf(cg);
while (try dst.toBase(cg)) {}
val_to_gpr: while (true) : (while (try val.toRegClass(false, .general_purpose, cg)) {}) {
const val_mcv = val.tracking(cg).short;
switch (val_mcv) {
else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }),
.immediate => |val_imm| {
const val_op: Immediate = if (std.math.cast(u32, val_imm)) |val_uimm32|
.u(val_uimm32)
else if (std.math.cast(i32, @as(i64, @bitCast(val_imm)))) |val_simm32|
.s(val_simm32)
else
continue :val_to_gpr;
try cg.asmMemoryImmediate(
.{ ._, .mov },
try dst.tracking(cg).short.mem(cg, .{
.size = cg.memSize(val_ty),
.disp = disp,
}),
val_op,
);
},
.register => |val_reg| try dst.writeReg(disp, val_ty, registerAlias(
val_reg,
@intCast(val_ty.abiSize(cg.pt.zcu)),
), cg),
inline .register_pair, .register_triple, .register_quadruple => |val_regs| {
var part_disp = disp;
for (val_regs) |val_reg| {
try dst.writeReg(part_disp, val_ty, val_reg, cg);
part_disp += @divExact(val_reg.bitSize(), 8);
}
},
.register_offset => |val_reg_off| switch (val_reg_off.off) {
0 => try dst.writeReg(disp, val_ty, registerAlias(
val_reg_off.reg,
@intCast(val_ty.abiSize(cg.pt.zcu)),
), cg),
else => continue :val_to_gpr,
},
.lea_frame, .lea_symbol => continue :val_to_gpr,
.memory, .indirect, .load_frame, .load_symbol => {
var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address().offset(disp));
var val_ptr = try cg.tempInit(.usize, val_mcv.address());
var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) });
try dst_ptr.memcpy(&val_ptr, &len, cg);
try dst_ptr.die(cg);
try val_ptr.die(cg);
try len.die(cg);
},
}
break;
}
}
fn writeReg(dst: Temp, disp: i32, src_ty: Type, src_reg: Register, cg: *CodeGen) !void {
const src_rc = src_reg.class();
const src_abi_size = src_ty.abiSize(cg.pt.zcu);
const strat = try cg.moveStrategy(src_ty, src_rc, false);
if (src_rc == .x87 or std.math.isPowerOfTwo(src_abi_size)) {
try strat.write(cg, try dst.tracking(cg).short.mem(cg, .{
.size = .fromBitSize(@min(8 * src_abi_size, src_reg.bitSize())),
.disp = disp,
}), src_reg);
} else {
const frame_alloc: FrameAlloc = .initSpill(src_ty, cg.pt.zcu);
const frame_index = try cg.allocFrameIndex(frame_alloc);
const frame_size: Memory.Size = .fromSize(frame_alloc.abi_size);
try strat.write(cg, .{
.base = .{ .frame = frame_index },
.mod = .{ .rm = .{ .size = frame_size } },
}, src_reg);
var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address());
var src_ptr = try cg.tempInit(.usize, .{ .lea_frame = .{ .index = frame_index } });
var len = try cg.tempInit(.usize, .{ .immediate = src_abi_size });
try dst_ptr.memcpy(&src_ptr, &len, cg);
try dst_ptr.die(cg);
try src_ptr.die(cg);
try len.die(cg);
}
}
fn memcpy(dst: *Temp, src: *Temp, len: *Temp, cg: *CodeGen) !void {
while (true) for ([_]*Temp{ dst, src, len }, [_]Register{ .rdi, .rsi, .rcx }) |temp, reg| {
if (try temp.toReg(reg, cg)) break;
@ -28498,7 +28653,7 @@ fn tempAllocMem(cg: *CodeGen, ty: Type) !Temp {
return .{ .index = temp_index.toIndex() };
}
fn tempFromValue(cg: *CodeGen, ty: Type, value: MCValue) !Temp {
fn tempInit(cg: *CodeGen, ty: Type, value: MCValue) !Temp {
const temp_index = cg.next_temp_index;
temp_index.tracking(cg).* = .init(value);
cg.temp_type[@intFromEnum(temp_index)] = ty;
@ -28507,6 +28662,10 @@ fn tempFromValue(cg: *CodeGen, ty: Type, value: MCValue) !Temp {
return .{ .index = temp_index.toIndex() };
}
fn tempFromValue(cg: *CodeGen, value: Value) !Temp {
return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.genTypedValue(value));
}
fn tempFromOperand(
cg: *CodeGen,
inst: Air.Inst.Index,
@ -28549,7 +28708,7 @@ fn tempFromOperand(
else => break :init const_mcv,
}
});
return cg.tempFromValue(.fromInterned(ip.typeOf(val)), gop.value_ptr.short);
return cg.tempInit(.fromInterned(ip.typeOf(val)), gop.value_ptr.short);
}
const temp_index = cg.next_temp_index;
@ -29023,8 +29182,8 @@ const Select = struct {
return switch (spec.kind) {
.unused => null,
.any => try cg.tempAlloc(spec.type),
.cc => |cc| try cg.tempFromValue(spec.type, .{ .eflags = cc }),
.reg => |reg| try cg.tempFromValue(spec.type, .{ .register = reg }),
.cc => |cc| try cg.tempInit(spec.type, .{ .eflags = cc }),
.reg => |reg| try cg.tempInit(spec.type, .{ .register = reg }),
.rc => |rc| try cg.tempAllocReg(spec.type, regSetForRegClass(rc)),
.rc_mask => |rc_mask| try cg.tempAllocReg(spec.type, regSetForRegClass(rc_mask.rc)),
.mem => try cg.tempAllocMem(spec.type),
@ -29081,18 +29240,14 @@ const Select = struct {
break :res_scalar .{ scalar_int_ty, try pt.intValue_big(scalar_int_ty, big_int.toConst()) };
},
};
const res_ty, const res_val: Value = if (vector_len) |len| res: {
const vector_ty = try pt.vectorType(.{
const res_val: Value = if (vector_len) |len| .fromInterned(try pt.intern(.{ .aggregate = .{
.ty = (try pt.vectorType(.{
.len = len,
.child = res_scalar_ty.toIntern(),
});
const vector_val = try pt.intern(.{ .aggregate = .{
.ty = vector_ty.toIntern(),
.storage = .{ .repeated_elem = res_scalar_val.toIntern() },
} });
break :res .{ vector_ty, .fromInterned(vector_val) };
} else .{ res_scalar_ty, res_scalar_val };
return try cg.tempFromValue(res_ty, try cg.genTypedValue(res_val));
})).toIntern(),
.storage = .{ .repeated_elem = res_scalar_val.toIntern() },
} })) else res_scalar_val;
return try cg.tempFromValue(res_val);
},
.ref => |ref| ref.deref(s),
.ref_mask => |ref_mask| ref_mask.ref.deref(s),

View file

@ -2246,12 +2246,12 @@ test "matching captures causes union equivalence" {
}
test "signed enum tag with negative value" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
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_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
const Enum = enum(i8) {
a = -1,