authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-29 15:17:03-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-10-30 09:31:30+00:00
log402c14f86a97f47ca429565fddb7ffca5c08d873
treeee2b6f3f8dc7dd86f81c10d37840375de3d1c819
parent0dde70ef763d7d21ea2fe703deb253a9ae59cf72
signaturelock-open Commit is signed but in an unrecognized format.

aarch64: implement optional comparisons


8 files changed, 384 insertions(+), 291 deletions(-)

src/codegen/aarch64/Disassemble.zig+34-6
......@@ -74,10 +74,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
7474 dis.operands_separator,
7575 imm12,
7676 });
77 return if (!elide_shift) writer.print("{s}{f} #{s}", .{
77 return if (!elide_shift) writer.print("{s}{f} #{t}", .{
7878 dis.operands_separator,
7979 fmtCase(.lsl, dis.case),
80 @tagName(sh),
80 sh,
8181 });
8282 },
8383 .add_subtract_immediate_with_tags => |add_subtract_immediate_with_tags| {
......@@ -176,10 +176,10 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
176176 dis.operands_separator,
177177 imm16,
178178 });
179 return if (!elide_shift) writer.print("{s}{f} #{s}", .{
179 return if (!elide_shift) writer.print("{s}{f} #{t}", .{
180180 dis.operands_separator,
181181 fmtCase(.lsl, dis.case),
182 @tagName(hw),
182 hw,
183183 });
184184 },
185185 .bitfield => |bitfield| {
......@@ -833,8 +833,36 @@ pub fn printInstruction(dis: Disassemble, inst: aarch64.encoding.Instruction, wr
833833 },
834834 .rotate_right_into_flags => {},
835835 .evaluate_into_flags => {},
836 .conditional_compare_register => {},
837 .conditional_compare_immediate => {},
836 .conditional_compare_register => |conditional_compare_register| {
837 const group = conditional_compare_register.group;
838 const sf = group.sf;
839 return writer.print("{f}{s}{f}{s}{f}{s}#0x{x}{s}{f}", .{
840 fmtCase(group.op, dis.case),
841 dis.mnemonic_operands_separator,
842 group.Rn.decode(.{}).general(sf).fmtCase(dis.case),
843 dis.operands_separator,
844 group.Rm.decode(.{}).general(sf).fmtCase(dis.case),
845 dis.operands_separator,
846 @as(u4, @bitCast(group.nzcv)),
847 dis.operands_separator,
848 fmtCase(group.cond, dis.case),
849 });
850 },
851 .conditional_compare_immediate => |conditional_compare_immediate| {
852 const group = conditional_compare_immediate.group;
853 const sf = group.sf;
854 return writer.print("{f}{s}{f}{s}#0x{x}{s}#0x{x}{s}{f}", .{
855 fmtCase(group.op, dis.case),
856 dis.mnemonic_operands_separator,
857 group.Rn.decode(.{}).general(sf).fmtCase(dis.case),
858 dis.operands_separator,
859 group.imm5,
860 dis.operands_separator,
861 @as(u4, @bitCast(group.nzcv)),
862 dis.operands_separator,
863 fmtCase(group.cond, dis.case),
864 });
865 },
838866 .conditional_select => |conditional_select| {
839867 const decoded = conditional_select.decode();
840868 if (decoded == .unallocated) break :unallocated;
src/codegen/aarch64/Mir.zig+2-2
......@@ -136,7 +136,7 @@ pub fn emit(
136136 mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
137137 return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
138138 else
139 return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
139 return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
140140 mir.body[lazy_reloc.reloc.label],
141141 body_end - Instruction.size * (1 + lazy_reloc.reloc.label),
142142 lazy_reloc.reloc.addend,
......@@ -150,7 +150,7 @@ pub fn emit(
150150 else if (lf.cast(.macho)) |mf|
151151 try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
152152 else
153 return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {s}", .{@tagName(lf.tag)}),
153 return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
154154 mir.body[global_reloc.reloc.label],
155155 body_end - Instruction.size * (1 + global_reloc.reloc.label),
156156 global_reloc.reloc.addend,
src/codegen/aarch64/Select.zig+348-270
......@@ -961,7 +961,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
961961 .inst_index = undefined,
962962 };
963963 air_tag: switch (air.next().?) {
964 else => |air_tag| return isel.fail("unimplemented {s}", .{@tagName(air_tag)}),
964 else => |air_tag| return isel.fail("unimplemented {t}", .{air_tag}),
965965 .arg => {
966966 const arg_vi = isel.live_values.fetchRemove(air.inst_index).?.value;
967967 defer arg_vi.deref(isel);
......@@ -1117,12 +1117,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
11171117
11181118 const bin_op = air.data(air.inst_index).bin_op;
11191119 const ty = isel.air.typeOf(bin_op.lhs, ip);
1120 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
1120 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
11211121 const int_info = ty.intInfo(zcu);
11221122 switch (int_info.bits) {
11231123 0 => unreachable,
11241124 32, 64 => |bits| switch (int_info.signedness) {
1125 .signed => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1125 .signed => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
11261126 .unsigned => {
11271127 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
11281128 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,
11601160 try lhs_mat.finish(isel);
11611161 },
11621162 },
1163 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1163 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
11641164 }
11651165 }
11661166 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,
11721172 const bin_op = air.data(air.inst_index).bin_op;
11731173 const ty = isel.air.typeOf(bin_op.lhs, ip);
11741174 if (!ty.isRuntimeFloat()) {
1175 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
1175 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
11761176 const int_info = ty.intInfo(zcu);
11771177 switch (int_info.bits) {
11781178 0 => unreachable,
......@@ -1318,7 +1318,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
13181318 try rhs_lo64_mat.finish(isel);
13191319 try lhs_lo64_mat.finish(isel);
13201320 },
1321 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1321 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
13221322 }
13231323 } else switch (ty.floatBits(isel.target)) {
13241324 else => unreachable,
......@@ -1421,7 +1421,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
14211421
14221422 const bin_op = air.data(air.inst_index).bin_op;
14231423 const ty = isel.air.typeOf(bin_op.lhs, ip);
1424 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
1424 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
14251425 const int_info = ty.intInfo(zcu);
14261426 switch (int_info.signedness) {
14271427 .signed => switch (int_info.bits) {
......@@ -1443,7 +1443,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
14431443 try rhs_mat.finish(isel);
14441444 try lhs_mat.finish(isel);
14451445 },
1446 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1446 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
14471447 },
14481448 .unsigned => switch (int_info.bits) {
14491449 0 => unreachable,
......@@ -1545,8 +1545,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
15451545 try rhs_mat.finish(isel);
15461546 try lhs_mat.finish(isel);
15471547 },
1548 65...128 => return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1549 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1548 65...128 => return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }),
1549 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
15501550 },
15511551 }
15521552 }
......@@ -1558,7 +1558,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
15581558
15591559 const bin_op = air.data(air.inst_index).bin_op;
15601560 const ty = isel.air.typeOf(bin_op.lhs, ip);
1561 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
1561 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
15621562 const int_info = ty.intInfo(zcu);
15631563 switch (int_info.bits) {
15641564 0 => unreachable,
......@@ -1784,7 +1784,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
17841784 try rhs_mat.finish(isel);
17851785 try lhs_mat.finish(isel);
17861786 },
1787 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
1787 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
17881788 }
17891789 }
17901790 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,
18971897 const bin_op = air.data(air.inst_index).bin_op;
18981898 const ty = isel.air.typeOf(bin_op.lhs, ip);
18991899 if (!ty.isRuntimeFloat()) {
1900 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
1900 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
19011901 const int_info = ty.intInfo(zcu);
19021902 switch (int_info.bits) {
19031903 0 => unreachable,
......@@ -1970,7 +1970,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
19701970 else => unreachable,
19711971 .div_trunc, .div_exact => {},
19721972 .div_floor => switch (int_info.signedness) {
1973 .signed => return isel.fail("unimplemented {s}", .{@tagName(air_tag)}),
1973 .signed => return isel.fail("unimplemented {t}", .{air_tag}),
19741974 .unsigned => {},
19751975 },
19761976 }
......@@ -2012,7 +2012,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
20122012 try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
20132013 try call.finishParams(isel);
20142014 },
2015 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
2015 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
20162016 }
20172017 } else switch (ty.floatBits(isel.target)) {
20182018 else => unreachable,
......@@ -2169,9 +2169,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
21692169 const bin_op = air.data(air.inst_index).bin_op;
21702170 const ty = isel.air.typeOf(bin_op.lhs, ip);
21712171 if (!ty.isRuntimeFloat()) {
2172 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2172 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
21732173 const int_info = ty.intInfo(zcu);
2174 if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2174 if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
21752175
21762176 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
21772177 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,
24942494 const bin_op = air.data(air.inst_index).bin_op;
24952495 const ty = isel.air.typeOf(bin_op.lhs, ip);
24962496 if (!ty.isRuntimeFloat()) {
2497 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2497 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
24982498 const int_info = ty.intInfo(zcu);
2499 if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2499 if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
25002500
25012501 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
25022502 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,
29202920 else if (ty.isAbiInt(zcu))
29212921 ty.intInfo(zcu)
29222922 else
2923 return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2924 if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2923 return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
2924 if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
29252925
29262926 const lhs_vi = try isel.use(bin_op.lhs);
29272927 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,
29682968
29692969 const bin_op = air.data(air.inst_index).bin_op;
29702970 const ty = isel.air.typeOf(bin_op.lhs, ip);
2971 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
2971 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
29722972 const int_info = ty.intInfo(zcu);
29732973 switch (int_info.bits) {
29742974 0 => unreachable,
......@@ -3161,7 +3161,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
31613161 try lhs_hi64_mat.finish(isel);
31623162 break :unused;
31633163 },
3164 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
3164 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
31653165 }
31663166 }
31673167 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,
31743174 const ty = ty_op.ty.toType();
31753175 const int_info: std.builtin.Type.Int = int_info: {
31763176 if (ty_op.ty == .bool_type) break :int_info .{ .signedness = .unsigned, .bits = 1 };
3177 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3177 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
31783178 break :int_info ty.intInfo(zcu);
31793179 };
3180 if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3180 if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
31813181
31823182 const src_vi = try isel.use(ty_op.operand);
31833183 var offset = res_vi.value.size(isel);
......@@ -3302,7 +3302,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
33023302 }
33033303 },
33043304 128 => try dst_vi.value.move(isel, ty_op.operand),
3305 else => return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
3305 else => return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
33063306 }
33073307 } else if ((dst_ty.isPtrAtRuntime(zcu) or dst_ty.isAbiInt(zcu)) and (src_ty.isPtrAtRuntime(zcu) or src_ty.isAbiInt(zcu))) {
33083308 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,
33133313 src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu));
33143314 if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) {
33153315 try dst_vi.value.move(isel, ty_op.operand);
3316 } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
3316 } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
33173317 } else if (dst_tag == .float and src_tag == .float) {
33183318 assert(dst_ty.floatBits(isel.target) == src_ty.floatBits(isel.target));
33193319 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,
34833483 try call.paramAddress(isel, src_vi, .r1);
34843484 try call.paramAddress(isel, dst_vi.value, .r0);
34853485 try call.finishParams(isel);
3486 } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
3486 } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
34873487 } else if (dst_tag == .array and dst_ty.childType(zcu).isAbiInt(zcu) and src_ty.isAbiInt(zcu)) {
34883488 const dst_child_int_info = dst_ty.childType(zcu).intInfo(zcu);
34893489 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,
35103510 try call.paramAddress(isel, src_vi, .r1);
35113511 try call.paramAddress(isel, dst_vi.value, .r0);
35123512 try call.finishParams(isel);
3513 } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
3514 } else return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
3513 } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
3514 } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
35153515 }
35163516 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
35173517 },
......@@ -3737,7 +3737,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
37373737
37383738 const ty_op = air.data(air.inst_index).ty_op;
37393739 const ty = isel.air.typeOf(ty_op.operand, ip);
3740 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3740 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
37413741 const int_info = ty.intInfo(zcu);
37423742 switch (int_info.bits) {
37433743 0 => unreachable,
......@@ -3769,7 +3769,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
37693769 try src_hi64_mat.finish(isel);
37703770 try src_lo64_mat.finish(isel);
37713771 },
3772 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
3772 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
37733773 }
37743774 }
37753775 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,
37803780
37813781 const ty_op = air.data(air.inst_index).ty_op;
37823782 const ty = isel.air.typeOf(ty_op.operand, ip);
3783 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3783 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
37843784 const int_info = ty.intInfo(zcu);
37853785 switch (int_info.bits) {
37863786 0 => unreachable,
......@@ -3812,7 +3812,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
38123812 try src_hi64_mat.finish(isel);
38133813 try src_lo64_mat.finish(isel);
38143814 },
3815 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
3815 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
38163816 }
38173817 }
38183818 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,
38233823
38243824 const ty_op = air.data(air.inst_index).ty_op;
38253825 const ty = isel.air.typeOf(ty_op.operand, ip);
3826 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3826 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
38273827 const int_info = ty.intInfo(zcu);
3828 if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3828 if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
38293829
38303830 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
38313831 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,
38773877
38783878 const ty_op = air.data(air.inst_index).ty_op;
38793879 const ty = ty_op.ty.toType();
3880 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3880 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
38813881 const int_info = ty.intInfo(zcu);
3882 if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3882 if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
38833883
38843884 if (int_info.bits == 8) break :unused try res_vi.value.move(isel, ty_op.operand);
38853885 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,
39413941
39423942 const ty_op = air.data(air.inst_index).ty_op;
39433943 const ty = ty_op.ty.toType();
3944 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3944 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
39453945 const int_info = ty.intInfo(zcu);
3946 if (int_info.bits > 64) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
3946 if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
39473947
39483948 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
39493949 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,
42444244 const ty_op = air.data(air.inst_index).ty_op;
42454245 const ty = ty_op.ty.toType();
42464246 if (!ty.isRuntimeFloat()) {
4247 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
4247 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) });
42484248 switch (ty.intInfo(zcu).bits) {
42494249 0 => unreachable,
42504250 1...32 => {
......@@ -4306,7 +4306,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
43064306 try src_lo64_mat.finish(isel);
43074307 try src_hi64_mat.finish(isel);
43084308 },
4309 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) }),
4309 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),
43104310 }
43114311 } else switch (ty.floatBits(isel.target)) {
43124312 else => unreachable,
......@@ -4465,216 +4465,61 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
44654465 if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
44664466 defer res_vi.value.deref(isel);
44674467
4468 var bin_op = air.data(air.inst_index).bin_op;
4468 const bin_op = air.data(air.inst_index).bin_op;
44694469 const ty = isel.air.typeOf(bin_op.lhs, ip);
4470 if (!ty.isRuntimeFloat()) {
4471 const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
4472 .{ .signedness = .unsigned, .bits = 1 }
4473 else if (ty.isAbiInt(zcu))
4474 ty.intInfo(zcu)
4475 else if (ty.isPtrAtRuntime(zcu))
4476 .{ .signedness = .unsigned, .bits = 64 }
4477 else
4478 return isel.fail("bad {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
4479 if (int_info.bits > 256) return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(ty) });
4480
4481 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
4482 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) {
4470 switch (ip.indexToKey(ty.toIntern())) {
4471 else => {},
4472 .opt_type => |payload_ty| switch (air_tag) {
44834473 else => unreachable,
4484 .cmp_lt => switch (int_info.signedness) {
4485 .signed => .lt,
4486 .unsigned => .lo,
4487 },
4488 .cmp_lte => switch (int_info.bits) {
4489 else => unreachable,
4490 1...64 => switch (int_info.signedness) {
4491 .signed => .le,
4492 .unsigned => .ls,
4493 },
4494 65...128 => {
4495 std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
4496 continue :cond .cmp_gte;
4497 },
4498 },
4499 .cmp_eq => .eq,
4500 .cmp_gte => switch (int_info.signedness) {
4501 .signed => .ge,
4502 .unsigned => .hs,
4503 },
4504 .cmp_gt => switch (int_info.bits) {
4505 else => unreachable,
4506 1...64 => switch (int_info.signedness) {
4507 .signed => .gt,
4508 .unsigned => .hi,
4509 },
4510 65...128 => {
4511 std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
4512 continue :cond .cmp_lt;
4513 },
4474 .cmp_eq, .cmp_neq => if (!ty.optionalReprIsPayload(zcu)) {
4475 const lhs_vi = try isel.use(bin_op.lhs);
4476 const rhs_vi = try isel.use(bin_op.rhs);
4477 const payload_size = ZigType.abiSize(.fromInterned(payload_ty), zcu);
4478 var lhs_payload_part_it = lhs_vi.field(ty, 0, payload_size);
4479 const lhs_payload_part_vi = try lhs_payload_part_it.only(isel);
4480 var rhs_payload_part_it = rhs_vi.field(ty, 0, payload_size);
4481 const rhs_payload_part_vi = try rhs_payload_part_it.only(isel);
4482 const cmp_info = try isel.cmp(
4483 try res_vi.value.defReg(isel) orelse break :unused,
4484 .fromInterned(payload_ty),
4485 lhs_payload_part_vi.?,
4486 air_tag.toCmpOp().?,
4487 rhs_payload_part_vi.?,
4488 );
4489 try isel.emit(.@"b."(
4490 .vc,
4491 @intCast((isel.instructions.items.len + 1 - cmp_info.cset_label) << 2),
4492 ));
4493 var lhs_has_value_part_it = lhs_vi.field(ty, payload_size, 1);
4494 const lhs_has_value_part_vi = try lhs_has_value_part_it.only(isel);
4495 const lhs_has_value_part_mat = try lhs_has_value_part_vi.?.matReg(isel);
4496 var rhs_has_value_part_it = rhs_vi.field(ty, payload_size, 1);
4497 const rhs_has_value_part_vi = try rhs_has_value_part_it.only(isel);
4498 const rhs_has_value_part_mat = try rhs_has_value_part_vi.?.matReg(isel);
4499 try isel.emit(.ccmp(
4500 lhs_has_value_part_mat.ra.w(),
4501 .{ .register = rhs_has_value_part_mat.ra.w() },
4502 .{ .n = false, .z = false, .c = false, .v = true },
4503 .eq,
4504 ));
4505 try isel.emit(.ands(
4506 .wzr,
4507 lhs_has_value_part_mat.ra.w(),
4508 .{ .register = rhs_has_value_part_mat.ra.w() },
4509 ));
4510 try rhs_has_value_part_mat.finish(isel);
4511 try lhs_has_value_part_mat.finish(isel);
4512 break :unused;
45144513 },
4515 .cmp_neq => .ne,
4516 })));
4517
4518 const lhs_vi = try isel.use(bin_op.lhs);
4519 const rhs_vi = try isel.use(bin_op.rhs);
4520 var part_offset = lhs_vi.size(isel);
4521 while (part_offset > 0) {
4522 const part_size = @min(part_offset, 8);
4523 part_offset -= part_size;
4524 var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
4525 const lhs_part_vi = try lhs_part_it.only(isel);
4526 const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
4527 var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
4528 const rhs_part_vi = try rhs_part_it.only(isel);
4529 const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
4530 try isel.emit(switch (part_size) {
4531 else => unreachable,
4532 1...4 => switch (part_offset) {
4533 0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
4534 else => switch (air_tag) {
4535 else => unreachable,
4536 .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs(
4537 .wzr,
4538 lhs_part_mat.ra.w(),
4539 rhs_part_mat.ra.w(),
4540 ),
4541 .cmp_eq, .cmp_neq => .ccmp(
4542 lhs_part_mat.ra.w(),
4543 .{ .register = rhs_part_mat.ra.w() },
4544 .{ .n = false, .z = false, .c = false, .v = false },
4545 .eq,
4546 ),
4547 },
4548 },
4549 5...8 => switch (part_offset) {
4550 0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
4551 else => switch (air_tag) {
4552 else => unreachable,
4553 .cmp_lt, .cmp_lte, .cmp_gte, .cmp_gt => .sbcs(
4554 .xzr,
4555 lhs_part_mat.ra.x(),
4556 rhs_part_mat.ra.x(),
4557 ),
4558 .cmp_eq, .cmp_neq => .ccmp(
4559 lhs_part_mat.ra.x(),
4560 .{ .register = rhs_part_mat.ra.x() },
4561 .{ .n = false, .z = false, .c = false, .v = false },
4562 .eq,
4563 ),
4564 },
4565 },
4566 });
4567 try rhs_part_mat.finish(isel);
4568 try lhs_part_mat.finish(isel);
4569 }
4570 } else switch (ty.floatBits(isel.target)) {
4571 else => unreachable,
4572 16, 32, 64 => |bits| {
4573 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
4574 const need_fcvt = switch (bits) {
4575 else => unreachable,
4576 16 => !isel.target.cpu.has(.aarch64, .fullfp16),
4577 32, 64 => false,
4578 };
4579 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (air_tag) {
4580 else => unreachable,
4581 .cmp_lt => .lo,
4582 .cmp_lte => .ls,
4583 .cmp_eq => .eq,
4584 .cmp_gte => .ge,
4585 .cmp_gt => .gt,
4586 .cmp_neq => .ne,
4587 })));
4588
4589 const lhs_vi = try isel.use(bin_op.lhs);
4590 const rhs_vi = try isel.use(bin_op.rhs);
4591 const lhs_mat = try lhs_vi.matReg(isel);
4592 const rhs_mat = try rhs_vi.matReg(isel);
4593 const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
4594 defer if (need_fcvt) isel.freeReg(lhs_ra);
4595 const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
4596 defer if (need_fcvt) isel.freeReg(rhs_ra);
4597 try isel.emit(bits: switch (bits) {
4598 else => unreachable,
4599 16 => if (need_fcvt)
4600 continue :bits 32
4601 else
4602 .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }),
4603 32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }),
4604 64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }),
4605 });
4606 if (need_fcvt) {
4607 try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
4608 try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
4609 }
4610 try rhs_mat.finish(isel);
4611 try lhs_mat.finish(isel);
4612 },
4613 80, 128 => |bits| {
4614 const res_ra = try res_vi.value.defReg(isel) orelse break :unused;
4615
4616 try call.prepareReturn(isel);
4617 try call.returnFill(isel, .r0);
4618 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (air_tag) {
4619 else => unreachable,
4620 .cmp_lt => .lt,
4621 .cmp_lte => .le,
4622 .cmp_eq => .eq,
4623 .cmp_gte => {
4624 std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
4625 continue :cond .cmp_lte;
4626 },
4627 .cmp_gt => {
4628 std.mem.swap(Air.Inst.Ref, &bin_op.lhs, &bin_op.rhs);
4629 continue :cond .cmp_lt;
4630 },
4631 .cmp_neq => .ne,
4632 })));
4633 try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 }));
4634 try call.finishReturn(isel);
4635
4636 try call.prepareCallee(isel);
4637 try isel.global_relocs.append(gpa, .{
4638 .name = switch (bits) {
4639 else => unreachable,
4640 16 => "__cmphf2",
4641 32 => "__cmpsf2",
4642 64 => "__cmpdf2",
4643 80 => "__cmpxf2",
4644 128 => "__cmptf2",
4645 },
4646 .reloc = .{ .label = @intCast(isel.instructions.items.len) },
4647 });
4648 try isel.emit(.bl(0));
4649 try call.finishCallee(isel);
4650
4651 try call.prepareParams(isel);
4652 const lhs_vi = try isel.use(bin_op.lhs);
4653 const rhs_vi = try isel.use(bin_op.rhs);
4654 switch (bits) {
4655 else => unreachable,
4656 16, 32, 64, 128 => {
4657 try call.paramLiveOut(isel, rhs_vi, .v1);
4658 try call.paramLiveOut(isel, lhs_vi, .v0);
4659 },
4660 80 => {
4661 var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
4662 const rhs_hi16_vi = try rhs_hi16_it.only(isel);
4663 try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
4664 var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
4665 const rhs_lo64_vi = try rhs_lo64_it.only(isel);
4666 try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
4667 var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
4668 const lhs_hi16_vi = try lhs_hi16_it.only(isel);
4669 try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
4670 var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
4671 const lhs_lo64_vi = try lhs_lo64_it.only(isel);
4672 try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
4673 },
4674 }
4675 try call.finishParams(isel);
46764514 },
46774515 }
4516 _ = try isel.cmp(
4517 try res_vi.value.defReg(isel) orelse break :unused,
4518 ty,
4519 try isel.use(bin_op.lhs),
4520 air_tag.toCmpOp().?,
4521 try isel.use(bin_op.rhs),
4522 );
46784523 }
46794524 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
46804525 },
......@@ -5497,7 +5342,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
54975342 try src_mat.finish(isel);
54985343 },
54995344 };
5500 } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
5345 } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
55015346 }
55025347 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
55035348 },
......@@ -5517,7 +5362,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
55175362 .int => .integer_out_of_bounds,
55185363 .@"enum" => {
55195364 if (!dst_ty.isNonexhaustiveEnum(zcu)) {
5520 return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
5365 return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
55215366 }
55225367 break :panic_id .invalid_enum_value;
55235368 },
......@@ -5599,7 +5444,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
55995444 try src_mat.finish(isel);
56005445 }
56015446 }
5602 } else return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
5447 } else return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
56035448 }
56045449 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
56055450 },
......@@ -5610,7 +5455,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
56105455 const ty_op = air.data(air.inst_index).ty_op;
56115456 const dst_ty = ty_op.ty.toType();
56125457 const src_ty = isel.air.typeOf(ty_op.operand, ip);
5613 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) });
5458 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) });
56145459 const dst_int_info = dst_ty.intInfo(zcu);
56155460 switch (dst_int_info.bits) {
56165461 0 => unreachable,
......@@ -5683,9 +5528,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
56835528 try src_lo64_vi.?.liveOut(isel, dst_lo64_ra);
56845529 }
56855530 },
5686 else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
5531 else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
56875532 },
5688 else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
5533 else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
56895534 }
56905535 }
56915536 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,
64876332 const ty_op = air.data(air.inst_index).ty_op;
64886333 const dst_ty = ty_op.ty.toType();
64896334 const src_ty = isel.air.typeOf(ty_op.operand, ip);
6490 if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
6335 if (!dst_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
64916336 const dst_int_info = dst_ty.intInfo(zcu);
64926337 const src_bits = src_ty.floatBits(isel.target);
64936338 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,
66176462 }
66186463 try call.finishParams(isel);
66196464 },
6620 else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
6465 else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
66216466 }
66226467 }
66236468 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,
66306475 const dst_ty = ty_op.ty.toType();
66316476 const src_ty = isel.air.typeOf(ty_op.operand, ip);
66326477 const dst_bits = dst_ty.floatBits(isel.target);
6633 if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) });
6478 if (!src_ty.isAbiInt(zcu)) return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) });
66346479 const src_int_info = src_ty.intInfo(zcu);
66356480 switch (@max(dst_bits, src_int_info.bits)) {
66366481 0 => unreachable,
......@@ -6757,7 +6602,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
67576602 }
67586603 try call.finishParams(isel);
67596604 },
6760 else => return isel.fail("too big {s} {f} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
6605 else => return isel.fail("too big {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }),
67616606 }
67626607 }
67636608 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,
68366681
68376682 break :air_tag if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
68386683 },
6839 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(dst_ty) }),
6684 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(dst_ty) }),
68406685 }
68416686 };
68426687
......@@ -7157,7 +7002,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
71577002 else => unreachable,
71587003 },
71597004 }),
7160 else => return isel.fail("too big {s} {f}", .{ @tagName(air_tag), isel.fmtType(union_ty) }),
7005 else => return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(union_ty) }),
71617006 }
71627007 }
71637008 var payload_it = union_vi.value.field(union_ty, union_layout.payloadOffset(), union_layout.payload_size);
......@@ -8499,6 +8344,217 @@ fn ctzLimb(
84998344 }
85008345}
85018346
8347fn cmp(
8348 isel: *Select,
8349 res_ra: Register.Alias,
8350 ty: ZigType,
8351 orig_lhs_vi: Value.Index,
8352 op: std.math.CompareOperator,
8353 orig_rhs_vi: Value.Index,
8354) !struct { cset_label: usize } {
8355 var lhs_vi = orig_lhs_vi;
8356 var rhs_vi = orig_rhs_vi;
8357 if (!ty.isRuntimeFloat()) {
8358 const int_info: std.builtin.Type.Int = if (ty.toIntern() == .bool_type)
8359 .{ .signedness = .unsigned, .bits = 1 }
8360 else if (ty.isAbiInt(isel.pt.zcu))
8361 ty.intInfo(isel.pt.zcu)
8362 else if (ty.isPtrAtRuntime(isel.pt.zcu))
8363 .{ .signedness = .unsigned, .bits = 64 }
8364 else
8365 return isel.fail("bad cmp_{t} {f}", .{ op, isel.fmtType(ty) });
8366 if (int_info.bits > 256) return isel.fail("too big cmp_{t} {f}", .{ op, isel.fmtType(ty) });
8367 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
8368 .lt => switch (int_info.signedness) {
8369 .signed => .lt,
8370 .unsigned => .lo,
8371 },
8372 .lte => switch (int_info.bits) {
8373 else => unreachable,
8374 1...64 => switch (int_info.signedness) {
8375 .signed => .le,
8376 .unsigned => .ls,
8377 },
8378 65...128 => {
8379 std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
8380 continue :cond .gte;
8381 },
8382 },
8383 .eq => .eq,
8384 .gte => switch (int_info.signedness) {
8385 .signed => .ge,
8386 .unsigned => .hs,
8387 },
8388 .gt => switch (int_info.bits) {
8389 else => unreachable,
8390 1...64 => switch (int_info.signedness) {
8391 .signed => .gt,
8392 .unsigned => .hi,
8393 },
8394 65...128 => {
8395 std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
8396 continue :cond .lt;
8397 },
8398 },
8399 .neq => .ne,
8400 })));
8401 const cset_label = isel.instructions.items.len;
8402
8403 var part_offset = lhs_vi.size(isel);
8404 while (part_offset > 0) {
8405 const part_size = @min(part_offset, 8);
8406 part_offset -= part_size;
8407 var lhs_part_it = lhs_vi.field(ty, part_offset, part_size);
8408 const lhs_part_vi = try lhs_part_it.only(isel);
8409 const lhs_part_mat = try lhs_part_vi.?.matReg(isel);
8410 var rhs_part_it = rhs_vi.field(ty, part_offset, part_size);
8411 const rhs_part_vi = try rhs_part_it.only(isel);
8412 const rhs_part_mat = try rhs_part_vi.?.matReg(isel);
8413 try isel.emit(switch (part_size) {
8414 else => unreachable,
8415 1...4 => switch (part_offset) {
8416 0 => .subs(.wzr, lhs_part_mat.ra.w(), .{ .register = rhs_part_mat.ra.w() }),
8417 else => switch (op) {
8418 .lt, .lte, .gte, .gt => .sbcs(
8419 .wzr,
8420 lhs_part_mat.ra.w(),
8421 rhs_part_mat.ra.w(),
8422 ),
8423 .eq, .neq => .ccmp(
8424 lhs_part_mat.ra.w(),
8425 .{ .register = rhs_part_mat.ra.w() },
8426 .{ .n = false, .z = false, .c = false, .v = false },
8427 .eq,
8428 ),
8429 },
8430 },
8431 5...8 => switch (part_offset) {
8432 0 => .subs(.xzr, lhs_part_mat.ra.x(), .{ .register = rhs_part_mat.ra.x() }),
8433 else => switch (op) {
8434 .lt, .lte, .gte, .gt => .sbcs(
8435 .xzr,
8436 lhs_part_mat.ra.x(),
8437 rhs_part_mat.ra.x(),
8438 ),
8439 .eq, .neq => .ccmp(
8440 lhs_part_mat.ra.x(),
8441 .{ .register = rhs_part_mat.ra.x() },
8442 .{ .n = false, .z = false, .c = false, .v = false },
8443 .eq,
8444 ),
8445 },
8446 },
8447 });
8448 try rhs_part_mat.finish(isel);
8449 try lhs_part_mat.finish(isel);
8450 }
8451 return .{ .cset_label = cset_label };
8452 }
8453 switch (ty.floatBits(isel.target)) {
8454 else => unreachable,
8455 16, 32, 64 => |bits| {
8456 const need_fcvt = switch (bits) {
8457 else => unreachable,
8458 16 => !isel.target.cpu.has(.aarch64, .fullfp16),
8459 32, 64 => false,
8460 };
8461 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(switch (op) {
8462 .lt => .lo,
8463 .lte => .ls,
8464 .eq => .eq,
8465 .gte => .ge,
8466 .gt => .gt,
8467 .neq => .ne,
8468 })));
8469 const cset_label = isel.instructions.items.len;
8470
8471 const lhs_mat = try lhs_vi.matReg(isel);
8472 const rhs_mat = try rhs_vi.matReg(isel);
8473 const lhs_ra = if (need_fcvt) try isel.allocVecReg() else lhs_mat.ra;
8474 defer if (need_fcvt) isel.freeReg(lhs_ra);
8475 const rhs_ra = if (need_fcvt) try isel.allocVecReg() else rhs_mat.ra;
8476 defer if (need_fcvt) isel.freeReg(rhs_ra);
8477 try isel.emit(bits: switch (bits) {
8478 else => unreachable,
8479 16 => if (need_fcvt)
8480 continue :bits 32
8481 else
8482 .fcmp(lhs_ra.h(), .{ .register = rhs_ra.h() }),
8483 32 => .fcmp(lhs_ra.s(), .{ .register = rhs_ra.s() }),
8484 64 => .fcmp(lhs_ra.d(), .{ .register = rhs_ra.d() }),
8485 });
8486 if (need_fcvt) {
8487 try isel.emit(.fcvt(rhs_ra.s(), rhs_mat.ra.h()));
8488 try isel.emit(.fcvt(lhs_ra.s(), lhs_mat.ra.h()));
8489 }
8490 try rhs_mat.finish(isel);
8491 try lhs_mat.finish(isel);
8492 return .{ .cset_label = cset_label };
8493 },
8494 80, 128 => |bits| {
8495 try call.prepareReturn(isel);
8496 try call.returnFill(isel, .r0);
8497 try isel.emit(.csinc(res_ra.w(), .wzr, .wzr, .invert(cond: switch (op) {
8498 .lt => .lt,
8499 .lte => .le,
8500 .eq => .eq,
8501 .gte => {
8502 std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
8503 continue :cond .lte;
8504 },
8505 .gt => {
8506 std.mem.swap(Value.Index, &lhs_vi, &rhs_vi);
8507 continue :cond .lt;
8508 },
8509 .neq => .ne,
8510 })));
8511 const cset_label = isel.instructions.items.len;
8512 try isel.emit(.subs(.wzr, .w0, .{ .immediate = 0 }));
8513 try call.finishReturn(isel);
8514
8515 try call.prepareCallee(isel);
8516 try isel.global_relocs.append(isel.pt.zcu.gpa, .{
8517 .name = switch (bits) {
8518 else => unreachable,
8519 16 => "__cmphf2",
8520 32 => "__cmpsf2",
8521 64 => "__cmpdf2",
8522 80 => "__cmpxf2",
8523 128 => "__cmptf2",
8524 },
8525 .reloc = .{ .label = @intCast(isel.instructions.items.len) },
8526 });
8527 try isel.emit(.bl(0));
8528 try call.finishCallee(isel);
8529
8530 try call.prepareParams(isel);
8531 switch (bits) {
8532 else => unreachable,
8533 16, 32, 64, 128 => {
8534 try call.paramLiveOut(isel, rhs_vi, .v1);
8535 try call.paramLiveOut(isel, lhs_vi, .v0);
8536 },
8537 80 => {
8538 var rhs_hi16_it = rhs_vi.field(ty, 8, 8);
8539 const rhs_hi16_vi = try rhs_hi16_it.only(isel);
8540 try call.paramLiveOut(isel, rhs_hi16_vi.?, .r3);
8541 var rhs_lo64_it = rhs_vi.field(ty, 0, 8);
8542 const rhs_lo64_vi = try rhs_lo64_it.only(isel);
8543 try call.paramLiveOut(isel, rhs_lo64_vi.?, .r2);
8544 var lhs_hi16_it = lhs_vi.field(ty, 8, 8);
8545 const lhs_hi16_vi = try lhs_hi16_it.only(isel);
8546 try call.paramLiveOut(isel, lhs_hi16_vi.?, .r1);
8547 var lhs_lo64_it = lhs_vi.field(ty, 0, 8);
8548 const lhs_lo64_vi = try lhs_lo64_it.only(isel);
8549 try call.paramLiveOut(isel, lhs_lo64_vi.?, .r0);
8550 },
8551 }
8552 try call.finishParams(isel);
8553 return .{ .cset_label = cset_label };
8554 },
8555 }
8556}
8557
85028558fn loadReg(
85038559 isel: *Select,
85048560 ra: Register.Alias,
......@@ -9272,9 +9328,9 @@ pub const Value = struct {
92729328 opts: AddOrSubtractOptions,
92739329 ) !void {
92749330 const zcu = isel.pt.zcu;
9275 if (!ty.isAbiInt(zcu)) return isel.fail("bad {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
9331 if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ op, isel.fmtType(ty) });
92769332 const int_info = ty.intInfo(zcu);
9277 if (int_info.bits > 128) return isel.fail("too big {s} {f}", .{ @tagName(op), isel.fmtType(ty) });
9333 if (int_info.bits > 128) return isel.fail("too big {t} {f}", .{ op, isel.fmtType(ty) });
92789334 var part_offset = res_vi.size(isel);
92799335 var need_wrap = switch (opts.overflow) {
92809336 .@"unreachable" => false,
......@@ -10783,7 +10839,7 @@ pub const Value = struct {
1078310839 .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
1078410840 .payload => |payload| {
1078510841 constant = payload;
10786 constant_key = ip.indexToKey(payload);
10842 constant_key = ip.indexToKey(constant);
1078710843 continue :constant_key constant_key;
1078810844 },
1078910845 }
......@@ -11017,7 +11073,7 @@ pub const Value = struct {
1101711073 } } else .{ .undef = child_ty },
1101811074 else => |child| {
1101911075 constant = child;
11020 constant_key = ip.indexToKey(child);
11076 constant_key = ip.indexToKey(constant);
1102111077 continue :constant_key constant_key;
1102211078 },
1102311079 };
......@@ -11040,7 +11096,7 @@ pub const Value = struct {
1104011096 },
1104111097 .repeated_elem => |repeated_elem| {
1104211098 constant = repeated_elem;
11043 constant_key = ip.indexToKey(repeated_elem);
11099 constant_key = ip.indexToKey(constant);
1104411100 continue :constant_key constant_key;
1104511101 },
1104611102 };
......@@ -11099,6 +11155,28 @@ pub const Value = struct {
1109911155 }
1110011156 },
1110111157 },
11158 .un => |un| {
11159 const loaded_union = ip.loadUnionType(un.ty);
11160 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
11161 if (loaded_union.hasTag(ip)) {
11162 const tag_offset = union_layout.tagOffset();
11163 if (offset >= tag_offset and offset + size <= tag_offset + union_layout.tag_size) {
11164 offset -= tag_offset;
11165 continue :constant_key switch (ip.indexToKey(un.tag)) {
11166 else => unreachable,
11167 .int => |int| .{ .int = int },
11168 .enum_tag => |enum_tag| .{ .enum_tag = enum_tag },
11169 };
11170 }
11171 }
11172 const payload_offset = union_layout.payloadOffset();
11173 if (offset >= payload_offset and offset + size <= payload_offset + union_layout.payload_size) {
11174 offset -= payload_offset;
11175 constant = un.val;
11176 constant_key = ip.indexToKey(constant);
11177 continue :constant_key constant_key;
11178 }
11179 },
1110211180 else => {},
1110311181 }
1110411182 var buffer: [16]u8 = @splat(0);
......@@ -11259,7 +11337,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
1125911337 first = false;
1126011338 };
1126111339 if (reverse_live_registers.get(vi)) |ra| {
11262 try stderr.print("{s}{s}", .{ if (first) " <- " else ", ", @tagName(ra) });
11340 try stderr.print("{s}{t}", .{ if (first) " <- " else ", ", ra });
1126311341 first = false;
1126411342 }
1126511343 }
......@@ -11267,8 +11345,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
1126711345 switch (value.flags.parent_tag) {
1126811346 .unallocated => if (value.offset_from_parent != 0) try stderr.print(" +0x{x}", .{value.offset_from_parent}),
1126911347 .stack_slot => {
11270 try stderr.print(" [{s}, #{s}0x{x}", .{
11271 @tagName(value.parent_payload.stack_slot.base),
11348 try stderr.print(" [{t}, #{s}0x{x}", .{
11349 value.parent_payload.stack_slot.base,
1127211350 if (value.parent_payload.stack_slot.offset < 0) "-" else "",
1127311351 @abs(value.parent_payload.stack_slot.offset),
1127411352 });
......@@ -11282,7 +11360,7 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
1128211360 isel.fmtConstant(value.parent_payload.constant),
1128311361 }),
1128411362 }
11285 try stderr.print(" align({s})", .{@tagName(value.flags.alignment)});
11363 try stderr.print(" align({t})", .{value.flags.alignment});
1128611364 switch (value.flags.location_tag) {
1128711365 .large => try stderr.print(" size=0x{x} large", .{value.location_payload.large.size}),
1128811366 .small => {
......@@ -11292,8 +11370,8 @@ pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void {
1129211370 .unsigned => {},
1129311371 .signed => try stderr.writeAll(" signed"),
1129411372 }
11295 if (loc.hint != .zr) try stderr.print(" hint={s}", .{@tagName(loc.hint)});
11296 if (loc.register != .zr) try stderr.print(" loc={s}", .{@tagName(loc.register)});
11373 if (loc.hint != .zr) try stderr.print(" hint={t}", .{loc.hint});
11374 if (loc.register != .zr) try stderr.print(" loc={t}", .{loc.register});
1129711375 },
1129811376 }
1129911377 try stderr.print(" refs={d}\n", .{value.refs});
test/behavior/enum.zig-2
......@@ -899,7 +899,6 @@ test "enum value allocation" {
899899}
900900
901901test "enum literal casting to tagged union" {
902 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
903902 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
904903 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
905904
......@@ -935,7 +934,6 @@ test "enum literal casting to error union with payload enum" {
935934}
936935
937936test "constant enum initialization with differing sizes" {
938 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
939937 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
940938 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
941939 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
test/behavior/optional.zig-2
......@@ -149,7 +149,6 @@ test "nested optional field in struct" {
149149}
150150
151151test "equality compare optionals and non-optionals" {
152 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
153152 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
154153 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
155154 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
......@@ -209,7 +208,6 @@ test "equality compare optionals and non-optionals" {
209208}
210209
211210test "compare optionals with modified payloads" {
212 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
213211 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
214212
215213 var lhs: ?bool = false;
test/behavior/switch.zig-2
......@@ -576,7 +576,6 @@ test "switch with null and T peer types and inferred result location type" {
576576}
577577
578578test "switch prongs with cases with identical payload types" {
579 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
580579 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
581580 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
582581
......@@ -824,7 +823,6 @@ test "comptime inline switch" {
824823}
825824
826825test "switch capture peer type resolution" {
827 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
828826 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
829827
830828 const U = union(enum) {
test/behavior/tuple.zig-1
......@@ -496,7 +496,6 @@ test "anon tuple field referencing comptime var isn't comptime" {
496496}
497497
498498test "tuple with runtime value coerced into a slice with a sentinel" {
499 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
500499 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
501500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
502501 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
test/behavior/union.zig-6
......@@ -208,7 +208,6 @@ const Payload = union(Letter) {
208208};
209209
210210test "union with specified enum tag" {
211 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
212211 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
213212 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
214213 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
......@@ -219,7 +218,6 @@ test "union with specified enum tag" {
219218}
220219
221220test "packed union generates correctly aligned type" {
222 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
223221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
224222 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
225223 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -605,7 +603,6 @@ fn returnAnInt(x: i32) TaggedFoo {
605603}
606604
607605test "tagged union with all void fields but a meaningful tag" {
608 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
609606 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
610607 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
611608
......@@ -1032,7 +1029,6 @@ test "containers with single-field enums" {
10321029}
10331030
10341031test "@unionInit on union with tag but no fields" {
1035 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10361032 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10371033 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10381034
......@@ -1446,8 +1442,6 @@ test "access the tag of a global tagged union" {
14461442}
14471443
14481444test "coerce enum literal to union in result loc" {
1449 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1450
14511445 const U = union(enum) {
14521446 a,
14531447 b: u8,