authorgravatar for xtex@astrafall.orgxtex <xtex@astrafall.org> 2026-08-30 19:37:38+08:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-08-30 22:54:04+02:00
logefea52deb7b9ced2ce0b16169bd01bc563aab5b4
tree5f74675c7b7f55c70d5d2842ef6552f543d08ba6
parent107eafe87ebc24b2e644857f445188ffdd7a4799

loongarch: implement add/sub with overflow


1 files changed, 41 insertions(+), 4 deletions(-)

src/codegen/loongarch/Select.zig+41-4
...@@ -1026,7 +1026,7 @@ pub const Value = struct {...@@ -1026,7 +1026,7 @@ pub const Value = struct {
1026 }1026 }
10271027
1028 /// Defines a value with a register.1028 /// Defines a value with a register.
1029 /// Returned registers are free-ed.1029 /// Returned registers are free-ed and marked as written.
1030 /// Extension unchanged.1030 /// Extension unchanged.
1031 fn defReg(vi: Value.Index, isel: *Select) !?Register.Alias {1031 fn defReg(vi: Value.Index, isel: *Select) !?Register.Alias {
1032 const value = vi.get(isel);1032 const value = vi.get(isel);
...@@ -3573,6 +3573,26 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,...@@ -3573,6 +3573,26 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
3573 },3573 },
3574 }) else return isel.fail("unimplemented float", .{});3574 }) else return isel.fail("unimplemented float", .{});
3575 },3575 },
3576 .add_with_overflow, .sub_with_overflow => if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| {
3577 defer res_vi.value.deref(isel);
3578
3579 const ty_pl = air.data(air.inst_index).ty_pl;
3580 const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data;
3581 const ty = isel.air.typeOf(bin_op.lhs, ip);
3582 const lhs_vi = try isel.use(bin_op.lhs);
3583 const rhs_vi = try isel.use(bin_op.rhs);
3584 const ty_size = lhs_vi.size(isel);
3585
3586 const wrapped_vi = try res_vi.value.partExact(isel, 0, ty_size);
3587 const overflow_vi = try res_vi.value.partExact(isel, ty_size, 1);
3588 try isel.addOrSubtract(ty, wrapped_vi, switch (air_tag) {
3589 else => unreachable,
3590 .add_with_overflow => .add,
3591 .sub_with_overflow => .sub,
3592 }, lhs_vi, rhs_vi, .{
3593 .overflow = if (try overflow_vi.defReg(isel)) |overflow_ra| .{ .overflow_ra = overflow_ra } else .wrap,
3594 });
3595 },
3576 .not => if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {3596 .not => if (isel.live_values.fetchRemove(air.inst_index)) |res_vi| unused: {
3577 defer res_vi.value.deref(isel);3597 defer res_vi.value.deref(isel);
35783598
...@@ -3676,7 +3696,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,...@@ -3676,7 +3696,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
3676 try rhs_mat.finish(isel);3696 try rhs_mat.finish(isel);
3677 try lhs_mat.finish(isel);3697 try lhs_mat.finish(isel);
3678 },3698 },
3679 else => try isel.failUnimplemented("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }),3699 else => {
3700 _ = try res_vi.value.def(isel);
3701 try isel.failUnimplemented("too big {t} {f}", .{ air_tag, isel.fmtType(ty) });
3702 },
3680 }3703 }
3681 } else try isel.failUnimplemented("unimplemented float div", .{});3704 } else try isel.failUnimplemented("unimplemented float div", .{});
3682 },3705 },
...@@ -5996,7 +6019,7 @@ const AddOrSubtractOptions = struct {...@@ -5996,7 +6019,7 @@ const AddOrSubtractOptions = struct {
5996 @"unreachable",6019 @"unreachable",
5997 panic: Zcu.SimplePanicId,6020 panic: Zcu.SimplePanicId,
5998 wrap,6021 wrap,
5999 reg: Register,6022 overflow_ra: Register.Alias,
6000 };6023 };
6001};6024};
60026025
...@@ -6011,11 +6034,25 @@ fn addOrSubtract(...@@ -6011,11 +6034,25 @@ fn addOrSubtract(
6011 opts: AddOrSubtractOptions,6034 opts: AddOrSubtractOptions,
6012) !void {6035) !void {
6013 wip_mir_log.debug(" | # {t} ty = {f}, res = {f}, lhs = {f}, rhs = {f}, overflow = {t}", .{ op, isel.fmtType(ty), res_vi, lhs_vi, rhs_vi, opts.overflow });6036 wip_mir_log.debug(" | # {t} ty = {f}, res = {f}, lhs = {f}, rhs = {f}, overflow = {t}", .{ op, isel.fmtType(ty), res_vi, lhs_vi, rhs_vi, opts.overflow });
6014 // TODO: implement opts.overflow
6015 const zcu = isel.pt.zcu;6037 const zcu = isel.pt.zcu;
6016 assert(ty.isAbiInt(zcu));6038 assert(ty.isAbiInt(zcu));
6017 const int_info = ty.intInfo(zcu);6039 const int_info = ty.intInfo(zcu);
60186040
6041 switch (opts.overflow) {
6042 .wrap, .@"unreachable" => {},
6043 .overflow_ra => |overflow_ra| {
6044 const overflow_reg = if (overflow_ra.mod == .integer) overflow_ra.reg else try isel.allocRegForWrite(.int);
6045 defer if (overflow_ra.mod != .integer) isel.freeReg(overflow_reg);
6046 switch (op) {
6047 .add => try isel.cmp(overflow_reg, ty, res_vi, .lt, lhs_vi),
6048 .sub => try isel.cmp(overflow_reg, ty, res_vi, .gt, lhs_vi),
6049 }
6050 },
6051 .panic => {
6052 try isel.failUnimplemented("unimplemented {t} with {t}", .{ op, opts.overflow });
6053 },
6054 }
6055
6019 if (int_info.bits <= 32) {6056 if (int_info.bits <= 32) {
6020 try res_vi.reextendToGarbage(isel); // TODO optimize6057 try res_vi.reextendToGarbage(isel); // TODO optimize
6021 const res_reg = try res_vi.defRegMod(isel, .integer) orelse return;6058 const res_reg = try res_vi.defRegMod(isel, .integer) orelse return;