Skip to main content

lamina_codegen/x86_64/
abi.rs

1//! x86_64 ABI utilities for different platforms.
2
3use crate::abi::{Abi, common_call_stub, mangle_macos_name};
4use crate::target_support::os_uses_macho;
5use lamina_platform::TargetOperatingSystem;
6
7/// Platform-specific ABI utilities for x86_64 code generation.
8pub struct X86ABI {
9    target_os: TargetOperatingSystem,
10}
11
12impl X86ABI {
13    /// Creates a new ABI instance for the specified target OS.
14    pub fn new(target_os: TargetOperatingSystem) -> Self {
15        Self { target_os }
16    }
17
18    /// Returns the mangled function name with platform-specific prefix.
19    pub fn mangle_function_name(&self, name: &str) -> String {
20        match self.target_os {
21            os if os_uses_macho(os) => mangle_macos_name(name),
22            TargetOperatingSystem::Windows => {
23                if name == "main" {
24                    "main".to_string()
25                } else {
26                    name.to_string()
27                }
28            }
29            _ => name.to_string(),
30        }
31    }
32
33    /// Returns the global declaration directive for the main function.
34    pub fn get_main_global(&self) -> &'static str {
35        ".globl main"
36    }
37
38    /// Returns the argument registers for the target OS ABI.
39    ///
40    /// - Windows x64: rcx, rdx, r8, r9 (first 4 arguments)
41    /// - System V AMD64: rdi, rsi, rdx, rcx, r8, r9 (first 6 arguments)
42    pub fn arg_registers(&self) -> &'static [&'static str] {
43        match self.target_os {
44            TargetOperatingSystem::Windows => &["rcx", "rdx", "r8", "r9"],
45            _ => &["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
46        }
47    }
48
49    /// System V AMD64 ABI argument registers.
50    ///
51    /// Deprecated: use `arg_registers()` instead for platform-aware register selection.
52    pub const ARG_REGISTERS: &'static [&'static str] = &["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
53
54    /// Caller-saved registers that must be preserved by the caller if live across function calls.
55    pub const CALLER_SAVED_REGISTERS: &'static [&'static str] =
56        &["rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11"];
57
58    /// Callee-saved registers that are preserved by called functions.
59    pub const CALLEE_SAVED_REGISTERS: &'static [&'static str] =
60        &["rbx", "rbp", "r12", "r13", "r14", "r15"];
61}
62
63impl Abi for X86ABI {
64    fn target_os(&self) -> TargetOperatingSystem {
65        self.target_os
66    }
67
68    fn mangle_function_name(&self, name: &str) -> String {
69        X86ABI::mangle_function_name(self, name)
70    }
71
72    fn call_stub(&self, name: &str) -> Option<String> {
73        common_call_stub(name, self.target_os)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_x86_mangle_function_name() {
83        let abi_linux = X86ABI::new(TargetOperatingSystem::Linux);
84        assert_eq!(abi_linux.mangle_function_name("main"), "main");
85        assert_eq!(abi_linux.mangle_function_name("foo"), "foo");
86
87        let abi_macos = X86ABI::new(TargetOperatingSystem::MacOS);
88        assert_eq!(abi_macos.mangle_function_name("main"), "_main");
89        assert_eq!(abi_macos.mangle_function_name("foo"), "_foo");
90
91        let abi_ios = X86ABI::new(TargetOperatingSystem::IOS);
92        assert_eq!(abi_ios.mangle_function_name("main"), "_main");
93        assert_eq!(abi_ios.mangle_function_name("foo"), "_foo");
94
95        let abi_windows = X86ABI::new(TargetOperatingSystem::Windows);
96        assert_eq!(abi_windows.mangle_function_name("main"), "main");
97        assert_eq!(abi_windows.mangle_function_name("foo"), "foo");
98    }
99
100    #[test]
101    fn test_x86_call_stub() {
102        let abi_linux = X86ABI::new(TargetOperatingSystem::Linux);
103        assert_eq!(abi_linux.call_stub("malloc"), Some("malloc".to_string()));
104        assert_eq!(abi_linux.call_stub("dealloc"), Some("free".to_string()));
105        assert_eq!(abi_linux.call_stub("unknown"), None);
106
107        let abi_macos = X86ABI::new(TargetOperatingSystem::MacOS);
108        assert_eq!(abi_macos.call_stub("malloc"), Some("_malloc".to_string()));
109    }
110
111    #[test]
112    fn test_x86_arg_registers() {
113        let abi_linux = X86ABI::new(TargetOperatingSystem::Linux);
114        let regs = abi_linux.arg_registers();
115        assert_eq!(regs.len(), 6);
116        assert_eq!(regs[0], "rdi");
117        assert_eq!(regs[1], "rsi");
118        assert_eq!(regs[2], "rdx");
119        assert_eq!(regs[3], "rcx");
120        assert_eq!(regs[4], "r8");
121        assert_eq!(regs[5], "r9");
122
123        let abi_windows = X86ABI::new(TargetOperatingSystem::Windows);
124        let regs_win = abi_windows.arg_registers();
125        assert_eq!(regs_win.len(), 4);
126        assert_eq!(regs_win[0], "rcx");
127        assert_eq!(regs_win[1], "rdx");
128        assert_eq!(regs_win[2], "r8");
129        assert_eq!(regs_win[3], "r9");
130    }
131
132    #[test]
133    fn test_x86_caller_saved_registers() {
134        assert_eq!(X86ABI::CALLER_SAVED_REGISTERS.len(), 9);
135        assert!(X86ABI::CALLER_SAVED_REGISTERS.contains(&"rax"));
136        assert!(X86ABI::CALLER_SAVED_REGISTERS.contains(&"rdi"));
137    }
138
139    #[test]
140    fn test_x86_callee_saved_registers() {
141        assert_eq!(X86ABI::CALLEE_SAVED_REGISTERS.len(), 6);
142        assert!(X86ABI::CALLEE_SAVED_REGISTERS.contains(&"rbx"));
143        assert!(X86ABI::CALLEE_SAVED_REGISTERS.contains(&"rbp"));
144    }
145}