aarch64: implement optional comparisons

This commit is contained in:
Jacob Young 2025-10-29 15:17:03 -04:00 committed by Matthew Lugg
parent 0dde70ef76
commit 402c14f86a
No known key found for this signature in database
GPG key ID: 3F5B7DCCBF4AF02E
8 changed files with 384 additions and 291 deletions

View file

@ -74,10 +74,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
dis.operands_separator,
imm12,
});
return if (!elide_shift) writer.print("{s}{f} #{s}", .{
return if (!elide_shift) writer.print("{s}{f} #{t}", .{
dis.operands_separator,
fmtCase(.lsl, dis.case),
@tagName(sh),
sh,
});
},
.add_subtract_immediate_with_tags => |add_subtract_immediate_with_tags| {
@ -176,10 +176,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
dis.operands_separator,
imm16,
});
return if (!elide_shift) writer.print("{s}{f} #{s}", .{
return if (!elide_shift) writer.print("{s}{f} #{t}", .{
dis.operands_separator,
fmtCase(.lsl, dis.case),
@tagName(hw),
hw,
});
},
.bitfield => |bitfield| {
@ -833,8 +833,36 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
},
.rotate_right_into_flags => {},
.evaluate_into_flags => {},
.conditional_compare_register => {},
.conditional_compare_immediate => {},
.conditional_compare_register => |conditional_compare_register| {
const group = conditional_compare_register.group;
const sf = group.sf;
return writer.print("{f}{s}{f}{s}{f}{s}#0x{x}{s}{f}", .{
fmtCase(group.op, dis.case),
dis.mnemonic_operands_separator,
group.Rn.decode(.{}).general(sf).fmtCase(dis.case),
dis.operands_separator,
group.Rm.decode(.{}).general(sf).fmtCase(dis.case),
dis.operands_separator,
@as(u4, @bitCast(group.nzcv)),
dis.operands_separator,
fmtCase(group.cond, dis.case),
});
},
.conditional_compare_immediate => |conditional_compare_immediate| {
const group = conditional_compare_immediate.group;
const sf = group.sf;
return writer.print("{f}{s}{f}{s}#0x{x}{s}#0x{x}{s}{f}", .{
fmtCase(group.op, dis.case),
dis.mnemonic_operands_separator,
group.Rn.decode(.{}).general(sf).fmtCase(dis.case),
dis.operands_separator,
group.imm5,
dis.operands_separator,
@as(u4, @bitCast(group.nzcv)),
dis.operands_separator,
fmtCase(group.cond, dis.case),
});
},
.conditional_select => |conditional_select| {
const decoded = conditional_select.decode();
if (decoded == .unallocated) break :unallocated;

View file

@ -136,7 +136,7 @@ pub fn emit(
mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
else
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
mir.body[lazy_reloc.reloc.label],
body_end - Instruction.size * (1 + lazy_reloc.reloc.label),
lazy_reloc.reloc.addend,
@ -150,7 +150,7 @@ pub fn emit(
else if (lf.cast(.macho)) |mf|
try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
else
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
mir.body[global_reloc.reloc.label],
body_end - Instruction.size * (1 + global_reloc.reloc.label),
global_reloc.reloc.addend,

View file

@ -961,7 +961,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
.inst_index = undefined,
};
air_tag: switch (air.next().?) {
else => |air_tag| return isel.fail("unimplemented {s}", .{@tagName(air_tag)}),
else => |air_tag| return isel.fail("unimplemented {t}", .{air_tag}),
.arg => {
const arg_vi = isel.live_values.fetchRemove(air.inst_index).?.value;
defer arg_vi.deref(isel);
@ -1117,12 +1117,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
32, 64 => |bits| switch (int_info.signedness) {
.signed => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
.signed => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
.unsigned => {
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const lhs_vi = try isel.use(bin_op.lhs);
@ -1160,7 +1160,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try lhs_mat.finish(isel);
},
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -1172,7 +1172,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) {
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -1318,7 +1318,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try rhs_lo64_mat.finish(isel);
try lhs_lo64_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
} else switch (ty.floatBits(isel.target)) {
else => unreachable,
@ -1421,7 +1421,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.signedness) {
.signed => switch (int_info.bits) {
@ -1443,7 +1443,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try rhs_mat.finish(isel);
try lhs_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
},
.unsigned => switch (int_info.bits) {
0 => unreachable,
@ -1545,8 +1545,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try rhs_mat.finish(isel);
try lhs_mat.finish(isel);
},
65...128 => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
65...128 => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
},
}
}
@ -1558,7 +1558,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -1784,7 +1784,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try rhs_mat.finish(isel);
try lhs_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -1897,7 +1897,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) {
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -1970,7 +1970,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
else => unreachable,
.div_trunc, .div_exact => {},
.div_floor => switch (int_info.signedness) {
.signed => return isel.fail("unimplemented {s}", .{@tagName(air_tag)}),
.signed => return isel.fail("unimplemented {t}", .{air_tag}),
.unsigned => {},
},
}
@ -2012,7 +2012,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
try call.finishParams(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
} else switch (ty.floatBits(isel.target)) {
else => unreachable,
@ -2169,9 +2169,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) {
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const lhs_vi = try isel.use(bin_op.lhs);
@ -2494,9 +2494,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) {
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const lhs_vi = try isel.use(bin_op.lhs);
@ -2920,8 +2920,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
else if (ty.isAbiInt(zcu))
ty.intInfo(zcu)
else
return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const lhs_vi = try isel.use(bin_op.lhs);
const rhs_vi = try isel.use(bin_op.rhs);
@ -2968,7 +2968,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -3161,7 +3161,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try lhs_hi64_mat.finish(isel);
break :unused;
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -3174,10 +3174,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty = ty_op.ty.toType();
const int_info: std.builtin.Type.Int = int_info: {
if (ty_op.ty == .bool_type) break :int_info .{ .signedness = .unsigned, .bits = 1 };
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
break :int_info ty.intInfo(zcu);
};
if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const src_vi = try isel.use(ty_op.operand);
var offset = res_vi.value.size(isel);
@ -3302,7 +3302,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
},
128 => try dst_vi.value.move(isel, ty_op.operand),
else => return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
else => return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
}
} else if ((dst_ty.isPtrAtRuntime(zcu) or dst_ty.isAbiInt(zcu)) and (src_ty.isPtrAtRuntime(zcu) or src_ty.isAbiInt(zcu))) {
try dst_vi.value.move(isel, ty_op.operand);
@ -3313,7 +3313,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu));
if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) {
try dst_vi.value.move(isel, ty_op.operand);
} else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else if (dst_tag == .float and src_tag == .float) {
assert(dst_ty.floatBits(isel.target) == src_ty.floatBits(isel.target));
try dst_vi.value.move(isel, ty_op.operand);
@ -3483,7 +3483,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try call.paramAddress(isel, src_vi, .r1);
try call.paramAddress(isel, dst_vi.value, .r0);
try call.finishParams(isel);
} else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else if (dst_tag == .array and dst_ty.childType(zcu).isAbiInt(zcu) and src_ty.isAbiInt(zcu)) {
const dst_child_int_info = dst_ty.childType(zcu).intInfo(zcu);
const src_int_info = src_ty.intInfo(zcu);
@ -3510,8 +3510,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try call.paramAddress(isel, src_vi, .r1);
try call.paramAddress(isel, dst_vi.value, .r0);
try call.finishParams(isel);
} else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@ -3737,7 +3737,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = isel.air.typeOf(ty_op.operand, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -3769,7 +3769,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_hi64_mat.finish(isel);
try src_lo64_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -3780,7 +3780,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = isel.air.typeOf(ty_op.operand, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
switch (int_info.bits) {
0 => unreachable,
@ -3812,7 +3812,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_hi64_mat.finish(isel);
try src_lo64_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -3823,9 +3823,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = isel.air.typeOf(ty_op.operand, ip);
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const src_vi = try isel.use(ty_op.operand);
@ -3877,9 +3877,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = ty_op.ty.toType();
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
if (int_info.bits == 8) break :unused try res_vi.value.move(isel, ty_op.operand);
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
@ -3941,9 +3941,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = ty_op.ty.toType();
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const src_vi = try isel.use(ty_op.operand);
@ -4244,7 +4244,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const ty = ty_op.ty.toType();
if (!ty.isRuntimeFloat()) {
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
switch (ty.intInfo(zcu).bits) {
0 => unreachable,
1...32 => {
@ -4306,7 +4306,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_lo64_mat.finish(isel);
try src_hi64_mat.finish(isel);
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
}
} else switch (ty.floatBits(isel.target)) {
else => unreachable,
@ -4465,216 +4465,61 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
defer res_vi.value.deref(isel);
var bin_op = air.data(air.inst_index).bin_op;
const bin_op = air.data(air.inst_index).bin_op;
const ty = isel.air.typeOf(bin_op.lhs, ip);
if (!ty.isRuntimeFloat()) {
const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
.{ .signedness = .unsigned, .bits = 1 }
else if (ty.isAbiInt(zcu))
ty.intInfo(zcu)
else if (ty.isPtrAtRuntime(zcu))
.{ .signedness = .unsigned, .bits = 64 }
else
return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
if (int_info.bits > 256) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) {
switch (ip.indexToKey(ty.toIntern())) {
else => {},
.opt_type => |payload_ty| switch (air_tag) {
else => unreachable,
.cmp_lt => switch (int_info.signedness) {
.signed => .lt,
.unsigned => .lo,
},
.cmp_lte => switch (int_info.bits) {
else => unreachable,
1...64 => switch (int_info.signedness) {
.signed => .le,
.unsigned => .ls,
},
65...128 => {
std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
continue :cond .cmp_gte;
},
},
.cmp_eq => .eq,
.cmp_gte => switch (int_info.signedness) {
.signed => .ge,
.unsigned => .hs,
},
.cmp_gt => switch (int_info.bits) {
else => unreachable,
1...64 => switch (int_info.signedness) {
.signed => .gt,
.unsigned => .hi,
},
65...128 => {
std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
continue :cond .cmp_lt;
},
},
.cmp_neq => .ne,
})));
.cmp_eq, .cmp_neq => if (!ty.optionalReprIsPayload(zcu)) {
const lhs_vi = try isel.use(bin_op.lhs);
const rhs_vi = try isel.use(bin_op.rhs);
var part_offset = lhs_vi.size(isel);
while (part_offset > 0) {
const part_size = @min(part_offset, 8);
part_offset -= part_size;
var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
const lhs_part_vi = try lhs_part_it.only(isel);
const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
const rhs_part_vi = try rhs_part_it.only(isel);
const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
try isel.emit(switch (part_size) {
else => unreachable,
1...4 => switch (part_offset) {
0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
else => switch (air_tag) {
else => unreachable,
.cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs(
const payload_size = ZigType.abiSize(.fromInterned(payload_ty), zcu);
var lhs_payload_part_it = lhs_vi.field(ty, 0, payload_size);
const lhs_payload_part_vi = try lhs_payload_part_it.only(isel);
var rhs_payload_part_it = rhs_vi.field(ty, 0, payload_size);
const rhs_payload_part_vi = try rhs_payload_part_it.only(isel);
const cmp_info = try isel.cmp(
try res_vi.value.defReg(isel) orelse break :unused,
.fromInterned(payload_ty),
lhs_payload_part_vi.?,
air_tag.toCmpOp().?,
rhs_payload_part_vi.?,
);
try isel.emit(.@"b."(
.vc,
@intCast((isel.instructions.items.len + 1 - cmp_info.cset_label) << 2),
));
var lhs_has_value_part_it = lhs_vi.field(ty, payload_size, 1);
const lhs_has_value_part_vi = try lhs_has_value_part_it.only(isel);
const lhs_has_value_part_mat = try lhs_has_value_part_vi.?.matReg(isel);
var rhs_has_value_part_it = rhs_vi.field(ty, payload_size, 1);
const rhs_has_value_part_vi = try rhs_has_value_part_it.only(isel);
const rhs_has_value_part_mat = try rhs_has_value_part_vi.?.matReg(isel);
try isel.emit(.ccmp(
lhs_has_value_part_mat.ra.w(),
.{ .register = rhs_has_value_part_mat.ra.w() },
.{ .n = false, .z = false, .c = false, .v = true },
.eq,
));
try isel.emit(.ands(
.wzr,
lhs_part_mat.ra.w(),
rhs_part_mat.ra.w(),
),
.cmp_eq, .cmp_neq => .ccmp(
lhs_part_mat.ra.w(),
.{ .register = rhs_part_mat.ra.w() },
.{ .n = false, .z = false, .c = false, .v = false },
.eq,
),
lhs_has_value_part_mat.ra.w(),
.{ .register = rhs_has_value_part_mat.ra.w() },
));
try rhs_has_value_part_mat.finish(isel);
try lhs_has_value_part_mat.finish(isel);
break :unused;
},
},
5...8 => switch (part_offset) {
0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
else => switch (air_tag) {
else => unreachable,
.cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs(
.xzr,
lhs_part_mat.ra.x(),
rhs_part_mat.ra.x(),
),
.cmp_eq, .cmp_neq => .ccmp(
lhs_part_mat.ra.x(),
.{ .register = rhs_part_mat.ra.x() },
.{ .n = false, .z = false, .c = false, .v = false },
.eq,
),
},
},
});
try rhs_part_mat.finish(isel);
try lhs_part_mat.finish(isel);
}
} else switch (ty.floatBits(isel.target)) {
else => unreachable,
16, 32, 64 => |bits| {
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
const need_fcvt = switch (bits) {
else => unreachable,
16 => !isel.target.cpu.has(.aarch64, .fullfp16),
32, 64 => false,
};
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (air_tag) {
else => unreachable,
.cmp_lt => .lo,
.cmp_lte => .ls,
.cmp_eq => .eq,
.cmp_gte => .ge,
.cmp_gt => .gt,
.cmp_neq => .ne,
})));
const lhs_vi = try isel.use(bin_op.lhs);
const rhs_vi = try isel.use(bin_op.rhs);
const lhs_mat = try lhs_vi.matReg(isel);
const rhs_mat = try rhs_vi.matReg(isel);
const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
defer if (need_fcvt) isel.freeReg(lhs_ra);
const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
defer if (need_fcvt) isel.freeReg(rhs_ra);
try isel.emit(bits: switch (bits) {
else => unreachable,
16 => if (need_fcvt)
continue :bits 32
else
.fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }),
32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }),
64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }),
});
if (need_fcvt) {
try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
}
try rhs_mat.finish(isel);
try lhs_mat.finish(isel);
},
80, 128 => |bits| {
const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
try call.prepareReturn(isel);
try call.returnFill(isel, .r0);
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) {
else => unreachable,
.cmp_lt => .lt,
.cmp_lte => .le,
.cmp_eq => .eq,
.cmp_gte => {
std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
continue :cond .cmp_lte;
},
.cmp_gt => {
std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
continue :cond .cmp_lt;
},
.cmp_neq => .ne,
})));
try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 }));
try call.finishReturn(isel);
try call.prepareCallee(isel);
try isel.global_relocs.append(gpa, .{
.name = switch (bits) {
else => unreachable,
16 => "__cmphf2",
32 => "__cmpsf2",
64 => "__cmpdf2",
80 => "__cmpxf2",
128 => "__cmptf2",
},
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
try call.finishCallee(isel);
try call.prepareParams(isel);
const lhs_vi = try isel.use(bin_op.lhs);
const rhs_vi = try isel.use(bin_op.rhs);
switch (bits) {
else => unreachable,
16, 32, 64, 128 => {
try call.paramLiveOut(isel, rhs_vi, .v1);
try call.paramLiveOut(isel, lhs_vi, .v0);
},
80 => {
var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
const rhs_hi16_vi = try rhs_hi16_it.only(isel);
try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
const rhs_lo64_vi = try rhs_lo64_it.only(isel);
try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
const lhs_hi16_vi = try lhs_hi16_it.only(isel);
try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
const lhs_lo64_vi = try lhs_lo64_it.only(isel);
try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
},
}
try call.finishParams(isel);
},
}
_ = try isel.cmp(
try res_vi.value.defReg(isel) orelse break :unused,
ty,
try isel.use(bin_op.lhs),
air_tag.toCmpOp().?,
try isel.use(bin_op.rhs),
);
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@ -5497,7 +5342,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_mat.finish(isel);
},
};
} else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@ -5517,7 +5362,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
.int => .integer_out_of_bounds,
.@"enum" => {
if (!dst_ty.isNonexhaustiveEnum(zcu)) {
return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
}
break :panic_id .invalid_enum_value;
},
@ -5599,7 +5444,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_mat.finish(isel);
}
}
} else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
} else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
@ -5610,7 +5455,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const dst_ty = ty_op.ty.toType();
const src_ty = isel.air.typeOf(ty_op.operand, ip);
if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
if (!dst_ty.isAbiInt(zcu) or !src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
const dst_int_info = dst_ty.intInfo(zcu);
switch (dst_int_info.bits) {
0 => unreachable,
@ -5683,9 +5528,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
}
},
else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
},
else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -6487,7 +6332,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const ty_op = air.data(air.inst_index).ty_op;
const dst_ty = ty_op.ty.toType();
const src_ty = isel.air.typeOf(ty_op.operand, ip);
if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
const dst_int_info = dst_ty.intInfo(zcu);
const src_bits = src_ty.floatBits(isel.target);
switch (@max(dst_int_info.bits, src_bits)) {
@ -6617,7 +6462,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
try call.finishParams(isel);
},
else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -6630,7 +6475,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
const dst_ty = ty_op.ty.toType();
const src_ty = isel.air.typeOf(ty_op.operand, ip);
const dst_bits = dst_ty.floatBits(isel.target);
if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
const src_int_info = src_ty.intInfo(zcu);
switch (@max(dst_bits, src_int_info.bits)) {
0 => unreachable,
@ -6757,7 +6602,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
}
try call.finishParams(isel);
},
else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
}
}
if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
@ -6836,7 +6681,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
},
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(dst_ty) }),
}
};
@ -7157,7 +7002,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
else => unreachable,
},
}),
else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(union_ty) }),
else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(union_ty) }),
}
}
var payload_it = union_vi.value.field(union_ty, union_layout.payloadOffset(), union_layout.payload_size);
@ -8499,6 +8344,217 @@ fn ctzLimb(
}
}
fn cmp(
isel: *Select,
res_ra: Register.Alias,
ty: ZigType,
orig_lhs_vi: Value.Index,
op: std.math.CompareOperator,
orig_rhs_vi: Value.Index,
) !struct { cset_label: usize } {
var lhs_vi = orig_lhs_vi;
var rhs_vi = orig_rhs_vi;
if (!ty.isRuntimeFloat()) {
const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
.{ .signedness = .unsigned, .bits = 1 }
else if (ty.isAbiInt(isel.pt.zcu))
ty.intInfo(isel.pt.zcu)
else if (ty.isPtrAtRuntime(isel.pt.zcu))
.{ .signedness = .unsigned, .bits = 64 }
else
return isel.fail("bad cmp_{t} {f}", .{ op, isel.fmtType(ty) });
if (int_info.bits > 256) return isel.fail("too big cmp_{t} {f}", .{ op, isel.fmtType(ty) });
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
.lt => switch (int_info.signedness) {
.signed => .lt,
.unsigned => .lo,
},
.lte => switch (int_info.bits) {
else => unreachable,
1...64 => switch (int_info.signedness) {
.signed => .le,
.unsigned => .ls,
},
65...128 => {
std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
continue :cond .gte;
},
},
.eq => .eq,
.gte => switch (int_info.signedness) {
.signed => .ge,
.unsigned => .hs,
},
.gt => switch (int_info.bits) {
else => unreachable,
1...64 => switch (int_info.signedness) {
.signed => .gt,
.unsigned => .hi,
},
65...128 => {
std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
continue :cond .lt;
},
},
.neq => .ne,
})));
const cset_label = isel.instructions.items.len;
var part_offset = lhs_vi.size(isel);
while (part_offset > 0) {
const part_size = @min(part_offset, 8);
part_offset -= part_size;
var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
const lhs_part_vi = try lhs_part_it.only(isel);
const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
const rhs_part_vi = try rhs_part_it.only(isel);
const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
try isel.emit(switch (part_size) {
else => unreachable,
1...4 => switch (part_offset) {
0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
else => switch (op) {
.lt, .lte, .gte, .gt => .sbcs(
.wzr,
lhs_part_mat.ra.w(),
rhs_part_mat.ra.w(),
),
.eq, .neq => .ccmp(
lhs_part_mat.ra.w(),
.{ .register = rhs_part_mat.ra.w() },
.{ .n = false, .z = false, .c = false, .v = false },
.eq,
),
},
},
5...8 => switch (part_offset) {
0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
else => switch (op) {
.lt, .lte, .gte, .gt => .sbcs(
.xzr,
lhs_part_mat.ra.x(),
rhs_part_mat.ra.x(),
),
.eq, .neq => .ccmp(
lhs_part_mat.ra.x(),
.{ .register = rhs_part_mat.ra.x() },
.{ .n = false, .z = false, .c = false, .v = false },
.eq,
),
},
},
});
try rhs_part_mat.finish(isel);
try lhs_part_mat.finish(isel);
}
return .{ .cset_label = cset_label };
}
switch (ty.floatBits(isel.target)) {
else => unreachable,
16, 32, 64 => |bits| {
const need_fcvt = switch (bits) {
else => unreachable,
16 => !isel.target.cpu.has(.aarch64, .fullfp16),
32, 64 => false,
};
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (op) {
.lt => .lo,
.lte => .ls,
.eq => .eq,
.gte => .ge,
.gt => .gt,
.neq => .ne,
})));
const cset_label = isel.instructions.items.len;
const lhs_mat = try lhs_vi.matReg(isel);
const rhs_mat = try rhs_vi.matReg(isel);
const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
defer if (need_fcvt) isel.freeReg(lhs_ra);
const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
defer if (need_fcvt) isel.freeReg(rhs_ra);
try isel.emit(bits: switch (bits) {
else => unreachable,
16 => if (need_fcvt)
continue :bits 32
else
.fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }),
32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }),
64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }),
});
if (need_fcvt) {
try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
}
try rhs_mat.finish(isel);
try lhs_mat.finish(isel);
return .{ .cset_label = cset_label };
},
80, 128 => |bits| {
try call.prepareReturn(isel);
try call.returnFill(isel, .r0);
try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
.lt => .lt,
.lte => .le,
.eq => .eq,
.gte => {
std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
continue :cond .lte;
},
.gt => {
std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
continue :cond .lt;
},
.neq => .ne,
})));
const cset_label = isel.instructions.items.len;
try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 }));
try call.finishReturn(isel);
try call.prepareCallee(isel);
try isel.global_relocs.append(isel.pt.zcu.gpa, .{
.name = switch (bits) {
else => unreachable,
16 => "__cmphf2",
32 => "__cmpsf2",
64 => "__cmpdf2",
80 => "__cmpxf2",
128 => "__cmptf2",
},
.reloc = .{ .label = @intCast(isel.instructions.items.len) },
});
try isel.emit(.bl(0));
try call.finishCallee(isel);
try call.prepareParams(isel);
switch (bits) {
else => unreachable,
16, 32, 64, 128 => {
try call.paramLiveOut(isel, rhs_vi, .v1);
try call.paramLiveOut(isel, lhs_vi, .v0);
},
80 => {
var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
const rhs_hi16_vi = try rhs_hi16_it.only(isel);
try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
const rhs_lo64_vi = try rhs_lo64_it.only(isel);
try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
const lhs_hi16_vi = try lhs_hi16_it.only(isel);
try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
const lhs_lo64_vi = try lhs_lo64_it.only(isel);
try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
},
}
try call.finishParams(isel);
return .{ .cset_label = cset_label };
},
}
}
fn loadReg(
isel: *Select,
ra: Register.Alias,
@ -9272,9 +9328,9 @@ pub const Value = struct {
opts: AddOrSubtractOptions,
) !void {
const zcu = isel.pt.zcu;
if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ op, isel.fmtType(ty) });
const int_info = ty.intInfo(zcu);
if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ op, isel.fmtType(ty) });
var part_offset = res_vi.size(isel);
var need_wrap = switch (opts.overflow) {
.@"unreachable" => false,
@ -10783,7 +10839,7 @@ pub const Value = struct {
.err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
.payload => |payload| {
constant = payload;
constant_key = ip.indexToKey(payload);
constant_key = ip.indexToKey(constant);
continue :constant_key constant_key;
},
}
@ -11017,7 +11073,7 @@ pub const Value = struct {
} } else .{ .undef = child_ty },
else => |child| {
constant = child;
constant_key = ip.indexToKey(child);
constant_key = ip.indexToKey(constant);
continue :constant_key constant_key;
},
};
@ -11040,7 +11096,7 @@ pub const Value = struct {
},
.repeated_elem => |repeated_elem| {
constant = repeated_elem;
constant_key = ip.indexToKey(repeated_elem);
constant_key = ip.indexToKey(constant);
continue :constant_key constant_key;
},
};
@ -11099,6 +11155,28 @@ pub const Value = struct {
}
},
},
.un => |un| {
const loaded_union = ip.loadUnionType(un.ty);
const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
if (loaded_union.hasTag(ip)) {
const tag_offset = union_layout.tagOffset();
if (offset >= tag_offset and offset + size <= tag_offset + union_layout.tag_size) {
offset -= tag_offset;
continue :constant_key switch (ip.indexToKey(un.tag)) {
else => unreachable,
.int => |int| .{ .int = int },
.enum_tag => |enum_tag| .{ .enum_tag = enum_tag },
};
}
}
const payload_offset = union_layout.payloadOffset();
if (offset >= payload_offset and offset + size <= payload_offset + union_layout.payload_size) {
offset -= payload_offset;
constant = un.val;
constant_key = ip.indexToKey(constant);
continue :constant_key constant_key;
}
},
else => {},
}
var buffer: [16]u8 = @splat(0);
@ -11259,7 +11337,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
first = false;
};
if (reverse_live_registers.get(vi)) |ra| {
try stderr.print("{s}{s}", .{ if (first) " <- " else ", ", @tagName(ra) });
try stderr.print("{s}{t}", .{ if (first) " <- " else ", ", ra });
first = false;
}
}
@ -11267,8 +11345,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
switch (value.flags.parent_tag) {
.unallocated => if (value.offset_from_parent != 0) try stderr.print(" +0x{x}", .{value.offset_from_parent}),
.stack_slot => {
try stderr.print(" [{s}, #{s}0x{x}", .{
@tagName(value.parent_payload.stack_slot.base),
try stderr.print(" [{t}, #{s}0x{x}", .{
value.parent_payload.stack_slot.base,
if (value.parent_payload.stack_slot.offset < 0) "-" else "",
@abs(value.parent_payload.stack_slot.offset),
});
@ -11282,7 +11360,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
isel.fmtConstant(value.parent_payload.constant),
}),
}
try stderr.print(" align({s})", .{@tagName(value.flags.alignment)});
try stderr.print(" align({t})", .{value.flags.alignment});
switch (value.flags.location_tag) {
.large => try stderr.print(" size=0x{x} large", .{value.location_payload.large.size}),
.small => {
@ -11292,8 +11370,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
.unsigned => {},
.signed => try stderr.writeAll(" signed"),
}
if (loc.hint != .zr) try stderr.print(" hint={s}", .{@tagName(loc.hint)});
if (loc.register != .zr) try stderr.print(" loc={s}", .{@tagName(loc.register)});
if (loc.hint != .zr) try stderr.print(" hint={t}", .{loc.hint});
if (loc.register != .zr) try stderr.print(" loc={t}", .{loc.register});
},
}
try stderr.print(" refs={d}\n", .{value.refs});

View file

@ -899,7 +899,6 @@ test "enum value allocation" {
}
test "enum literal casting to tagged union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -935,7 +934,6 @@ test "enum literal casting to error union with payload enum" {
}
test "constant enum initialization with differing sizes" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

View file

@ -149,7 +149,6 @@ test "nested optional field in struct" {
}
test "equality compare optionals and non-optionals" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -209,7 +208,6 @@ test "equality compare optionals and non-optionals" {
}
test "compare optionals with modified payloads" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var lhs: ?bool = false;

View file

@ -576,7 +576,6 @@ test "switch with null and T peer types and inferred result location type" {
}
test "switch prongs with cases with identical payload types" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -824,7 +823,6 @@ test "comptime inline switch" {
}
test "switch capture peer type resolution" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
const U = union(enum) {

View file

@ -496,7 +496,6 @@ test "anon tuple field referencing comptime var isn't comptime" {
}
test "tuple with runtime value coerced into a slice with a sentinel" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

View file

@ -208,7 +208,6 @@ const Payload = union(Letter) {
};
test "union with specified enum tag" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -219,7 +218,6 @@ test "union with specified enum tag" {
}
test "packed union generates correctly aligned type" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
@ -605,7 +603,6 @@ fn returnAnInt(x: i32) TaggedFoo {
}
test "tagged union with all void fields but a meaningful tag" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -1032,7 +1029,6 @@ test "containers with single-field enums" {
}
test "@unionInit on union with tag but no fields" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -1446,8 +1442,6 @@ test "access the tag of a global tagged union" {
}
test "coerce enum literal to union in result loc" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
const U = union(enum) {
a,
b: u8,