Commit 06058ed6f3

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-03-10 23:14:28
stage2 regalloc: replace Register.allocIndex with generic indexOfReg
* callee_preserved_regs and other ABI-specific information have been moved to the respective abi.zig files
1 parent 078037a
src/arch/aarch64/abi.zig
@@ -0,0 +1,20 @@
+const builtin = @import("builtin");
+const bits = @import("bits.zig");
+const Register = bits.Register;
+
+const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
+    pub const callee_preserved_regs = [_]Register{
+        .x20, .x21, .x22, .x23,
+        .x24, .x25, .x26, .x27,
+        .x28,
+    };
+} else struct {
+    pub const callee_preserved_regs = [_]Register{
+        .x19, .x20, .x21, .x22, .x23,
+        .x24, .x25, .x26, .x27, .x28,
+    };
+};
+pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_regs;
+
+pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
+pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
src/arch/aarch64/bits.zig
@@ -72,14 +72,6 @@ pub const Register = enum(u7) {
         };
     }
 
-    /// Returns the index into `callee_preserved_regs`.
-    pub fn allocIndex(self: Register) ?u4 {
-        inline for (callee_preserved_regs) |cpreg, i| {
-            if (self.id() == cpreg.id()) return i;
-        }
-        return null;
-    }
-
     pub fn dwarfLocOp(self: Register) u8 {
         return @as(u8, self.enc()) + DW.OP.reg0;
     }
@@ -87,23 +79,6 @@ pub const Register = enum(u7) {
 
 // zig fmt: on
 
-const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
-    pub const callee_preserved_regs = [_]Register{
-        .x20, .x21, .x22, .x23,
-        .x24, .x25, .x26, .x27,
-        .x28,
-    };
-} else struct {
-    pub const callee_preserved_regs = [_]Register{
-        .x19, .x20, .x21, .x22, .x23,
-        .x24, .x25, .x26, .x27, .x28,
-    };
-};
-pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_regs;
-
-pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
-pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
-
 test "Register.enc" {
     try testing.expectEqual(@as(u5, 0), Register.x0.enc());
     try testing.expectEqual(@as(u5, 0), Register.w0.enc());
src/arch/aarch64/CodeGen.zig
@@ -21,12 +21,21 @@ const DW = std.dwarf;
 const leb128 = std.leb;
 const log = std.log.scoped(.codegen);
 const build_options = @import("build_options");
-const RegisterManager = @import("../../register_manager.zig").RegisterManager;
+const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
+const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
 
 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
 const FnResult = @import("../../codegen.zig").FnResult;
 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
 
+const bits = @import("bits.zig");
+const abi = @import("abi.zig");
+const Register = bits.Register;
+const Instruction = bits.Instruction;
+const callee_preserved_regs = abi.callee_preserved_regs;
+const c_abi_int_param_regs = abi.c_abi_int_param_regs;
+const c_abi_int_return_regs = abi.c_abi_int_return_regs;
+
 const InnerError = error{
     OutOfMemory,
     CodegenFail,
@@ -73,7 +82,7 @@ branch_stack: *std.ArrayList(Branch),
 // Key is the block instruction
 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
 
-register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
+register_manager: RegisterManager = .{},
 /// Maps offset to what is stored there.
 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
 
@@ -1836,7 +1845,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
         .register => |reg| {
             // If it's in the registers table, need to associate the register with the
             // new instruction.
-            if (reg.allocIndex()) |index| {
+            if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
                 if (!self.register_manager.isRegFree(reg)) {
                     self.register_manager.registers[index] = inst;
                 }
@@ -2475,7 +2484,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
     const result: MCValue = result: {
         switch (info.return_value) {
             .register => |reg| {
-                if (Register.allocIndex(reg) == null) {
+                if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
                     // Save function return value in a callee saved register
                     break :result try self.copyToNewRegister(inst, info.return_value);
                 }
@@ -4017,12 +4026,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro
     return error.CodegenFail;
 }
 
-const Register = @import("bits.zig").Register;
-const Instruction = @import("bits.zig").Instruction;
-const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
-const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs;
-const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs;
-
 fn parseRegName(name: []const u8) ?Register {
     if (@hasDecl(Register, "parseRegName")) {
         return Register.parseRegName(name);
src/arch/arm/abi.zig
@@ -0,0 +1,6 @@
+const bits = @import("bits.zig");
+const Register = bits.Register;
+
+pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
+pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
+pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
src/arch/arm/bits.zig
@@ -162,14 +162,6 @@ pub const Register = enum(u5) {
         return @truncate(u4, @enumToInt(self));
     }
 
-    /// Returns the index into `callee_preserved_regs`.
-    pub fn allocIndex(self: Register) ?u4 {
-        inline for (callee_preserved_regs) |cpreg, i| {
-            if (self.id() == cpreg.id()) return i;
-        }
-        return null;
-    }
-
     pub fn dwarfLocOp(self: Register) u8 {
         return @as(u8, self.id()) + DW.OP.reg0;
     }
@@ -187,10 +179,6 @@ pub const Psr = enum {
     spsr,
 };
 
-pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
-pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
-pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
-
 /// Represents an instruction in the ARM instruction set architecture
 pub const Instruction = union(enum) {
     data_processing: packed struct {
src/arch/arm/CodeGen.zig
@@ -21,12 +21,22 @@ const DW = std.dwarf;
 const leb128 = std.leb;
 const log = std.log.scoped(.codegen);
 const build_options = @import("build_options");
-const RegisterManager = @import("../../register_manager.zig").RegisterManager;
+const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
+const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
 
 const FnResult = @import("../../codegen.zig").FnResult;
 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
 
+const bits = @import("bits.zig");
+const abi = @import("abi.zig");
+const Register = bits.Register;
+const Instruction = bits.Instruction;
+const Condition = bits.Condition;
+const callee_preserved_regs = abi.callee_preserved_regs;
+const c_abi_int_param_regs = abi.c_abi_int_param_regs;
+const c_abi_int_return_regs = abi.c_abi_int_return_regs;
+
 const InnerError = error{
     OutOfMemory,
     CodegenFail,
@@ -73,7 +83,7 @@ branch_stack: *std.ArrayList(Branch),
 // Key is the block instruction
 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
 
-register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
+register_manager: RegisterManager = .{},
 /// Maps offset to what is stored there.
 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
 /// Tracks the current instruction allocated to the compare flags
@@ -1561,7 +1571,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
         .register => |reg| {
             // If it's in the registers table, need to associate the register with the
             // new instruction.
-            if (reg.allocIndex()) |index| {
+            if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
                 if (!self.register_manager.isRegFree(reg)) {
                     self.register_manager.registers[index] = inst;
                 }
@@ -2652,7 +2662,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
     const result: MCValue = result: {
         switch (info.return_value) {
             .register => |reg| {
-                if (Register.allocIndex(reg) == null) {
+                if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
                     // Save function return value in a callee saved register
                     break :result try self.copyToNewRegister(inst, info.return_value);
                 }
@@ -4495,13 +4505,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro
     return error.CodegenFail;
 }
 
-const Register = @import("bits.zig").Register;
-const Instruction = @import("bits.zig").Instruction;
-const Condition = @import("bits.zig").Condition;
-const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
-const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs;
-const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs;
-
 fn parseRegName(name: []const u8) ?Register {
     if (@hasDecl(Register, "parseRegName")) {
         return Register.parseRegName(name);
src/arch/riscv64/abi.zig
@@ -0,0 +1,6 @@
+const bits = @import("bits.zig");
+const Register = bits.Register;
+
+pub const callee_preserved_regs = [_]Register{
+    .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
+};
src/arch/riscv64/bits.zig
@@ -409,14 +409,6 @@ pub const Register = enum(u6) {
         return @truncate(u5, @enumToInt(self));
     }
 
-    /// Returns the index into `callee_preserved_regs`.
-    pub fn allocIndex(self: Register) ?u4 {
-        inline for (callee_preserved_regs) |cpreg, i| {
-            if (self.id() == cpreg.id()) return i;
-        }
-        return null;
-    }
-
     pub fn dwarfLocOp(reg: Register) u8 {
         return @as(u8, reg.id()) + DW.OP.reg0;
     }
@@ -424,10 +416,6 @@ pub const Register = enum(u6) {
 
 // zig fmt: on
 
-pub const callee_preserved_regs = [_]Register{
-    .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
-};
-
 test "serialize instructions" {
     const Testcase = struct {
         inst: Instruction,
src/arch/riscv64/CodeGen.zig
@@ -21,12 +21,19 @@ const DW = std.dwarf;
 const leb128 = std.leb;
 const log = std.log.scoped(.codegen);
 const build_options = @import("build_options");
-const RegisterManager = @import("../../register_manager.zig").RegisterManager;
+const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
+const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
 
 const FnResult = @import("../../codegen.zig").FnResult;
 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
 
+const bits = @import("bits.zig");
+const abi = @import("abi.zig");
+const Register = bits.Register;
+const Instruction = abi.Instruction;
+const callee_preserved_regs = abi.callee_preserved_regs;
+
 const InnerError = error{
     OutOfMemory,
     CodegenFail,
@@ -75,7 +82,7 @@ branch_stack: *std.ArrayList(Branch),
 // Key is the block instruction
 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
 
-register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
+register_manager: RegisterManager = .{},
 /// Maps offset to what is stored there.
 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
 
@@ -1230,7 +1237,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
         .register => |reg| {
             // If it's in the registers table, need to associate the register with the
             // new instruction.
-            if (reg.allocIndex()) |index| {
+            if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
                 if (!self.register_manager.isRegFree(reg)) {
                     self.register_manager.registers[index] = inst;
                 }
@@ -1545,7 +1552,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
     const result: MCValue = result: {
         switch (info.return_value) {
             .register => |reg| {
-                if (Register.allocIndex(reg) == null) {
+                if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
                     // Save function return value in a callee saved register
                     break :result try self.copyToNewRegister(inst, info.return_value);
                 }
@@ -2549,10 +2556,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro
     return error.CodegenFail;
 }
 
-const Register = @import("bits.zig").Register;
-const Instruction = @import("bits.zig").Instruction;
-const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
-
 fn parseRegName(name: []const u8) ?Register {
     if (@hasDecl(Register, "parseRegName")) {
         return Register.parseRegName(name);
src/arch/x86/bits.zig
@@ -4,16 +4,16 @@ const DW = std.dwarf;
 // zig fmt: off
 pub const Register = enum(u8) {
     // 0 through 7, 32-bit registers. id is int value
-    eax, ecx, edx, ebx, esp, ebp, esi, edi, 
+    eax, ecx, edx, ebx, esp, ebp, esi, edi,
 
     // 8-15, 16-bit registers. id is int value - 8.
     ax, cx, dx, bx, sp, bp, si, di,
-    
+
     // 16-23, 8-bit registers. id is int value - 16.
     al, cl, dl, bl, ah, ch, dh, bh,
 
     /// Returns the bit-width of the register.
-    pub fn size(self: @This()) u7 {
+    pub fn size(self: Register) u7 {
         return switch (@enumToInt(self)) {
             0...7 => 32,
             8...15 => 16,
@@ -25,22 +25,10 @@ pub const Register = enum(u8) {
     /// Returns the register's id. This is used in practically every opcode the
     /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
     /// instruction, and is used in the R/M byte.
-    pub fn id(self: @This()) u3 {
+    pub fn id(self: Register) u3 {
         return @truncate(u3, @enumToInt(self));
     }
 
-    /// Returns the index into `callee_preserved_regs`.
-    pub fn allocIndex(self: Register) ?u4 {
-        return switch (self) {
-            .eax, .ax, .al => 0,
-            .ecx, .cx, .cl => 1,
-            .edx, .dx, .dl => 2,
-            .esi, .si  => 3,
-            .edi, .di => 4,
-            else => null,
-        };
-    }
-
     /// Convert from any register to its 32 bit alias.
     pub fn to32(self: Register) Register {
         return @intToEnum(Register, @as(u8, self.id()));
@@ -56,7 +44,6 @@ pub const Register = enum(u8) {
         return @intToEnum(Register, @as(u8, self.id()) + 16);
     }
 
-
     pub fn dwarfLocOp(reg: Register) u8 {
         return switch (reg.to32()) {
             .eax => DW.OP.reg0,
src/arch/x86_64/abi.zig
@@ -2,6 +2,7 @@ const std = @import("std");
 const Type = @import("../../type.zig").Type;
 const Target = std.Target;
 const assert = std.debug.assert;
+const Register = @import("bits.zig").Register;
 
 pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
 
@@ -370,3 +371,9 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
         else => unreachable,
     }
 }
+
+/// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered
+/// and when the callee returns.
+pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
+pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
+pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
src/arch/x86_64/bits.zig
@@ -30,14 +30,14 @@ pub const Register = enum(u7) {
 
     // 16 through 31, 32-bit registers. 24-31 are extended.
     // id is int value - 16.
-    eax, ecx, edx, ebx, esp, ebp, esi, edi, 
+    eax, ecx, edx, ebx, esp, ebp, esi, edi,
     r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
 
     // 32-47, 16-bit registers. 40-47 are extended.
     // id is int value - 32.
     ax, cx, dx, bx, sp, bp, si, di,
     r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
-    
+
     // 48-63, 8-bit registers. 56-63 are extended.
     // id is int value - 48.
     al, cl, dl, bl, ah, ch, dh, bh,
@@ -81,20 +81,6 @@ pub const Register = enum(u7) {
         return @truncate(u3, @enumToInt(self));
     }
 
-    /// Returns the index into `callee_preserved_regs`.
-    pub fn allocIndex(self: Register) ?u4 {
-        return switch (self) {
-            .rcx, .ecx, .cx, .cl => 0,
-            .rsi, .esi, .si => 1,
-            .rdi, .edi, .di => 2,
-            .r8, .r8d, .r8w, .r8b => 3,
-            .r9, .r9d, .r9w, .r9b => 4,
-            .r10, .r10d, .r10w, .r10b => 5,
-            .r11, .r11d, .r11w, .r11b => 6,
-            else => null,
-        };
-    }
-
     /// Convert from any register to its 64 bit alias.
     pub fn to64(self: Register) Register {
         return @intToEnum(Register, self.id());
@@ -142,13 +128,6 @@ pub const Register = enum(u7) {
 
 // zig fmt: on
 
-/// TODO this set is actually a set of caller-saved registers.
-/// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered
-/// and when the callee returns.
-pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
-pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
-pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
-
 /// Encoding helper functions for x86_64 instructions
 ///
 /// Many of these helpers do very little, but they can help make things
src/arch/x86_64/CodeGen.zig
@@ -27,6 +27,13 @@ const Type = @import("../../type.zig").Type;
 const TypedValue = @import("../../TypedValue.zig");
 const Value = @import("../../value.zig").Value;
 
+const bits = @import("bits.zig");
+const abi = @import("abi.zig");
+const Register = bits.Register;
+const callee_preserved_regs = abi.callee_preserved_regs;
+const c_abi_int_param_regs = abi.c_abi_int_param_regs;
+const c_abi_int_return_regs = abi.c_abi_int_return_regs;
+
 const InnerError = error{
     OutOfMemory,
     CodegenFail,
@@ -2336,7 +2343,7 @@ fn reuseOperand(
         .register => |reg| {
             // If it's in the registers table, need to associate the register with the
             // new instruction.
-            if (reg.allocIndex()) |index| {
+            if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
                 if (!self.register_manager.isRegFree(reg)) {
                     self.register_manager.registers[index] = inst;
                 }
@@ -3483,7 +3490,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
     const result: MCValue = result: {
         switch (info.return_value) {
             .register => |reg| {
-                if (Register.allocIndex(reg) == null) {
+                if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
                     // Save function return value in a callee saved register
                     break :result try self.copyToRegisterWithInstTracking(
                         inst,
@@ -5966,18 +5973,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro
     return error.CodegenFail;
 }
 
-const Register = @import("bits.zig").Register;
-
-const Instruction = void;
-
-const Condition = void;
-
-const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
-
-const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs;
-
-const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs;
-
 fn parseRegName(name: []const u8) ?Register {
     if (@hasDecl(Register, "parseRegName")) {
         return Register.parseRegName(name);
src/arch/x86_64/Emit.zig
@@ -6,6 +6,7 @@ const Emit = @This();
 const std = @import("std");
 const assert = std.debug.assert;
 const bits = @import("bits.zig");
+const abi = @import("abi.zig");
 const leb128 = std.leb;
 const link = @import("../../link.zig");
 const log = std.log.scoped(.codegen);
@@ -265,7 +266,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I
     const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data;
     const regs = data.regs;
     var disp: u32 = data.disp + 8;
-    for (bits.callee_preserved_regs) |reg, i| {
+    for (abi.callee_preserved_regs) |reg, i| {
         if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
         if (tag == .push) {
             try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
src/arch/x86_64/PrintMir.zig
@@ -5,6 +5,7 @@ const Print = @This();
 const std = @import("std");
 const assert = std.debug.assert;
 const bits = @import("bits.zig");
+const abi = @import("abi.zig");
 const leb128 = std.leb;
 const link = @import("../../link.zig");
 const log = std.log.scoped(.codegen);
@@ -188,7 +189,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(print: *const Print, tag: Mir.Inst.Tag,
     var disp: u32 = data.disp + 8;
     if (regs == 0) return w.writeAll("no regs from callee_preserved_regs\n");
     var printed_first_reg = false;
-    for (bits.callee_preserved_regs) |reg, i| {
+    for (abi.callee_preserved_regs) |reg, i| {
         if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
         if (printed_first_reg) try w.writeAll("  ");
         printed_first_reg = true;
src/register_manager.zig
@@ -61,7 +61,7 @@ pub fn RegisterManager(
         }
 
         fn getRegisterMask(reg: Register) ?FreeRegInt {
-            const index = reg.allocIndex() orelse return null;
+            const index = indexOfRegIntoTracked(reg) orelse return null;
             const shift = @intCast(ShiftInt, index);
             const mask = @as(FreeRegInt, 1) << shift;
             return mask;
@@ -82,6 +82,17 @@ pub fn RegisterManager(
             self.free_registers |= mask;
         }
 
+        pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) {
+            inline for (callee_preserved_regs) |cpreg, i| {
+                if (reg.id() == cpreg.id()) return i;
+            }
+            return null;
+        }
+
+        pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt {
+            return indexOfReg(callee_preserved_regs, reg);
+        }
+
         /// Returns true when this register is not tracked
         pub fn isRegFree(self: Self, reg: Register) bool {
             const mask = getRegisterMask(reg) orelse return true;
@@ -157,7 +168,7 @@ pub fn RegisterManager(
 
                 if (insts[j]) |inst| {
                     // Track the register
-                    const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
+                    const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
                     self.registers[index] = inst;
                     self.markRegUsed(reg);
                 }
@@ -196,7 +207,7 @@ pub fn RegisterManager(
 
                     regs[i] = reg;
                     self.markRegAllocated(reg);
-                    const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
+                    const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
                     if (insts[i]) |inst| {
                         // Track the register
                         if (self.isRegFree(reg)) {
@@ -235,7 +246,7 @@ pub fn RegisterManager(
         /// corresponding instruction is passed, will also track this
         /// register.
         pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
-            const index = reg.allocIndex() orelse return;
+            const index = indexOfRegIntoTracked(reg) orelse return;
             self.markRegAllocated(reg);
 
             if (inst) |tracked_inst|
@@ -263,7 +274,7 @@ pub fn RegisterManager(
         /// instruction. Asserts that the register is free and no
         /// spilling is necessary.
         pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {
-            const index = reg.allocIndex() orelse return;
+            const index = indexOfRegIntoTracked(reg) orelse return;
             self.markRegAllocated(reg);
 
             assert(self.isRegFree(reg));
@@ -273,7 +284,7 @@ pub fn RegisterManager(
 
         /// Marks the specified register as free
         pub fn freeReg(self: *Self, reg: Register) void {
-            const index = reg.allocIndex() orelse return;
+            const index = indexOfRegIntoTracked(reg) orelse return;
             log.debug("freeing register {}", .{reg});
 
             self.registers[index] = undefined;
@@ -288,11 +299,8 @@ const MockRegister1 = enum(u2) {
     r2,
     r3,
 
-    pub fn allocIndex(self: MockRegister1) ?u2 {
-        inline for (callee_preserved_regs) |cpreg, i| {
-            if (self == cpreg) return i;
-        }
-        return null;
+    pub fn id(reg: MockRegister1) u2 {
+        return @enumToInt(reg);
     }
 
     const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 };
@@ -304,11 +312,8 @@ const MockRegister2 = enum(u2) {
     r2,
     r3,
 
-    pub fn allocIndex(self: MockRegister2) ?u2 {
-        inline for (callee_preserved_regs) |cpreg, i| {
-            if (self == cpreg) return i;
-        }
-        return null;
+    pub fn id(reg: MockRegister2) u2 {
+        return @enumToInt(reg);
     }
 
     const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
CMakeLists.txt
@@ -597,14 +597,17 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Mir.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig"
+    "${CMAKE_SOURCE_DIR}/src/arch/aarch64/abi.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/arm/CodeGen.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/arm/Emit.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/arm/Mir.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig"
+    "${CMAKE_SOURCE_DIR}/src/arch/arm/abi.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/riscv64/CodeGen.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Emit.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig"
+    "${CMAKE_SOURCE_DIR}/src/arch/riscv64/abi.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"
@@ -612,6 +615,7 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig"
     "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig"
+    "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig"
     "${CMAKE_SOURCE_DIR}/src/clang.zig"
     "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
     "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"