Commit 7c349da49c

Jacob Young <jacobly0@users.noreply.github.com>
2025-07-25 21:41:43
aarch64: implement complex switch prongs
1 parent a51cdf3
Changed files (3)
src
codegen
aarch64
test
src/codegen/aarch64/Select.zig
@@ -4328,7 +4328,6 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
             };
             var cond_mat: ?Value.Materialize = null;
             var cond_reg: Register = undefined;
-            var temp_reg: Register = undefined;
             var cases_it = switch_br.iterateCases();
             while (cases_it.next()) |case| {
                 const next_label = isel.instructions.items.len;
@@ -4342,11 +4341,10 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
                 if (cond_mat == null) {
                     var cond_vi = try isel.use(switch_br.operand);
                     cond_mat = try cond_vi.matReg(isel);
-                    const temp_ra = try isel.allocIntReg();
-                    cond_reg, temp_reg = switch (cond_int_info.bits) {
+                    cond_reg = switch (cond_int_info.bits) {
                         else => unreachable,
-                        1...32 => .{ cond_mat.?.ra.w(), temp_ra.w() },
-                        33...64 => .{ cond_mat.?.ra.x(), temp_ra.x() },
+                        1...32 => cond_mat.?.ra.w(),
+                        33...64 => cond_mat.?.ra.x(),
                     };
                 }
                 if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned(
@@ -4387,17 +4385,45 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
                     ) else high_bigint.toInt(i64) catch
                         return isel.fail("too big case range end: {f}", .{isel.fmtConstant(high_val)});
 
+                    const adjusted_ra = switch (low_int) {
+                        0 => cond_mat.?.ra,
+                        else => try isel.allocIntReg(),
+                    };
+                    defer if (adjusted_ra != cond_mat.?.ra) isel.freeReg(adjusted_ra);
+                    const adjusted_reg = switch (cond_int_info.bits) {
+                        else => unreachable,
+                        1...32 => adjusted_ra.w(),
+                        33...64 => adjusted_ra.x(),
+                    };
                     const delta_int = high_int -% low_int;
-                    if (case_range_index > 0) {
-                        return isel.fail("case range", .{});
-                    } else if (case.items.len > 0) {
-                        return isel.fail("case range", .{});
+                    if (case_range_index | case.items.len > 0) {
+                        if (std.math.cast(u5, delta_int)) |pos_imm| try isel.emit(.ccmp(
+                            adjusted_reg,
+                            .{ .immediate = pos_imm },
+                            .{ .n = false, .z = true, .c = false, .v = false },
+                            if (case_range_index > 0) .hi else .ne,
+                        )) else if (std.math.cast(u5, -delta_int)) |neg_imm| try isel.emit(.ccmn(
+                            adjusted_reg,
+                            .{ .immediate = neg_imm },
+                            .{ .n = false, .z = true, .c = false, .v = false },
+                            if (case_range_index > 0) .hi else .ne,
+                        )) else {
+                            const imm_ra = try isel.allocIntReg();
+                            defer isel.freeReg(imm_ra);
+                            const imm_reg = switch (cond_int_info.bits) {
+                                else => unreachable,
+                                1...32 => imm_ra.w(),
+                                33...64 => imm_ra.x(),
+                            };
+                            try isel.emit(.ccmp(
+                                cond_reg,
+                                .{ .register = imm_reg },
+                                .{ .n = false, .z = true, .c = false, .v = false },
+                                if (case_range_index > 0) .hi else .ne,
+                            ));
+                            try isel.movImmediate(imm_reg, @bitCast(delta_int));
+                        }
                     } else {
-                        const adjusted_reg = switch (low_int) {
-                            0 => cond_reg,
-                            else => temp_reg,
-                        };
-
                         if (std.math.cast(u12, delta_int)) |pos_imm| try isel.emit(.subs(
                             zero_reg,
                             adjusted_reg,
@@ -4421,41 +4447,55 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
                             adjusted_reg,
                             .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
                         )) else {
-                            try isel.movImmediate(temp_reg, @bitCast(delta_int));
-                            try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = temp_reg }));
+                            const imm_ra = try isel.allocIntReg();
+                            defer isel.freeReg(imm_ra);
+                            const imm_reg = switch (cond_int_info.bits) {
+                                else => unreachable,
+                                1...32 => imm_ra.w(),
+                                33...64 => imm_ra.x(),
+                            };
+                            try isel.emit(.subs(zero_reg, adjusted_reg, .{ .register = imm_reg }));
+                            try isel.movImmediate(imm_reg, @bitCast(delta_int));
                         }
+                    }
 
-                        switch (low_int) {
-                            0 => {},
-                            else => {
-                                if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
-                                    adjusted_reg,
-                                    cond_reg,
-                                    .{ .immediate = pos_imm },
-                                )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
-                                    adjusted_reg,
-                                    cond_reg,
-                                    .{ .immediate = neg_imm },
-                                )) else if (if (@as(i12, @truncate(low_int)) == 0)
-                                    std.math.cast(u12, low_int >> 12)
-                                else
-                                    null) |pos_imm_lsr_12| try isel.emit(.sub(
-                                    adjusted_reg,
-                                    cond_reg,
-                                    .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
-                                )) else if (if (@as(i12, @truncate(-low_int)) == 0)
-                                    std.math.cast(u12, -low_int >> 12)
-                                else
-                                    null) |neg_imm_lsr_12| try isel.emit(.add(
-                                    adjusted_reg,
-                                    cond_reg,
-                                    .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
-                                )) else {
-                                    try isel.movImmediate(temp_reg, @bitCast(low_int));
-                                    try isel.emit(.subs(adjusted_reg, cond_reg, .{ .register = temp_reg }));
-                                }
-                            },
-                        }
+                    switch (low_int) {
+                        0 => {},
+                        else => {
+                            if (std.math.cast(u12, low_int)) |pos_imm| try isel.emit(.sub(
+                                adjusted_reg,
+                                cond_reg,
+                                .{ .immediate = pos_imm },
+                            )) else if (std.math.cast(u12, -low_int)) |neg_imm| try isel.emit(.add(
+                                adjusted_reg,
+                                cond_reg,
+                                .{ .immediate = neg_imm },
+                            )) else if (if (@as(i12, @truncate(low_int)) == 0)
+                                std.math.cast(u12, low_int >> 12)
+                            else
+                                null) |pos_imm_lsr_12| try isel.emit(.sub(
+                                adjusted_reg,
+                                cond_reg,
+                                .{ .shifted_immediate = .{ .immediate = pos_imm_lsr_12, .lsl = .@"12" } },
+                            )) else if (if (@as(i12, @truncate(-low_int)) == 0)
+                                std.math.cast(u12, -low_int >> 12)
+                            else
+                                null) |neg_imm_lsr_12| try isel.emit(.add(
+                                adjusted_reg,
+                                cond_reg,
+                                .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
+                            )) else {
+                                const imm_ra = try isel.allocIntReg();
+                                defer isel.freeReg(imm_ra);
+                                const imm_reg = switch (cond_int_info.bits) {
+                                    else => unreachable,
+                                    1...32 => imm_ra.w(),
+                                    33...64 => imm_ra.x(),
+                                };
+                                try isel.emit(.sub(adjusted_reg, cond_reg, .{ .register = imm_reg }));
+                                try isel.movImmediate(imm_reg, @bitCast(low_int));
+                            }
+                        },
                     }
                 }
                 var case_item_index = case.items.len;
@@ -4483,13 +4523,20 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
                             .{ .n = false, .z = true, .c = false, .v = false },
                             .ne,
                         )) else {
-                            try isel.movImmediate(temp_reg, @bitCast(item_int));
+                            const imm_ra = try isel.allocIntReg();
+                            defer isel.freeReg(imm_ra);
+                            const imm_reg = switch (cond_int_info.bits) {
+                                else => unreachable,
+                                1...32 => imm_ra.w(),
+                                33...64 => imm_ra.x(),
+                            };
                             try isel.emit(.ccmp(
                                 cond_reg,
-                                .{ .register = temp_reg },
+                                .{ .register = imm_reg },
                                 .{ .n = false, .z = true, .c = false, .v = false },
                                 .ne,
                             ));
+                            try isel.movImmediate(imm_reg, @bitCast(item_int));
                         }
                     } else {
                         if (std.math.cast(u12, item_int)) |pos_imm| try isel.emit(.subs(
@@ -4515,16 +4562,20 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) !void {
                             cond_reg,
                             .{ .shifted_immediate = .{ .immediate = neg_imm_lsr_12, .lsl = .@"12" } },
                         )) else {
-                            try isel.movImmediate(temp_reg, @bitCast(item_int));
-                            try isel.emit(.subs(zero_reg, cond_reg, .{ .register = temp_reg }));
+                            const imm_ra = try isel.allocIntReg();
+                            defer isel.freeReg(imm_ra);
+                            const imm_reg = switch (cond_int_info.bits) {
+                                else => unreachable,
+                                1...32 => imm_ra.w(),
+                                33...64 => imm_ra.x(),
+                            };
+                            try isel.emit(.subs(zero_reg, cond_reg, .{ .register = imm_reg }));
+                            try isel.movImmediate(imm_reg, @bitCast(item_int));
                         }
                     }
                 }
             }
-            if (cond_mat) |mat| {
-                try mat.finish(isel);
-                isel.freeReg(temp_reg.alias);
-            }
+            if (cond_mat) |mat| try mat.finish(isel);
             if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
         },
         .@"try", .try_cold => {
test/behavior/inline_switch.zig
@@ -105,7 +105,6 @@ test "inline else enum" {
 }
 
 test "inline else int with gaps" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
 
test/behavior/switch.zig
@@ -8,7 +8,6 @@ const minInt = std.math.minInt;
 const maxInt = std.math.maxInt;
 
 test "switch with numbers" {
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;