r2smt_patch/
aarch64_encoding.rs1pub const ZERO_REGISTER_X: &str = "xzr";
30
31pub const ZERO_REGISTER_W: &str = "wzr";
33
34#[must_use]
43pub fn parse_xreg(raw: &str) -> Option<String> {
44 let trimmed = raw.trim().trim_end_matches(',').trim();
45 let lower = trimmed.to_ascii_lowercase();
46 if lower == ZERO_REGISTER_X || lower == ZERO_REGISTER_W {
47 return Some(lower);
48 }
49 if let Some(rest) = lower.strip_prefix('x').or_else(|| lower.strip_prefix('w'))
50 && let Ok(n) = rest.parse::<u8>()
51 && n <= 30
52 {
53 return Some(lower);
54 }
55 None
56}
57
58#[must_use]
60pub fn mov_reg(dst: &str, src: &str) -> String {
61 format!("mov {dst}, {src}")
62}
63
64#[must_use]
66pub fn mov_imm(dst: &str, imm: i64) -> String {
67 if imm < 0 {
68 format!("mov {dst}, #-{abs}", abs = imm.unsigned_abs())
69 } else {
70 format!("mov {dst}, #{imm}")
71 }
72}
73
74#[must_use]
76pub fn mvn_reg(dst: &str, src: &str) -> String {
77 format!("mvn {dst}, {src}")
78}
79
80#[must_use]
83pub fn neg_reg(dst: &str, src: &str) -> String {
84 format!("neg {dst}, {src}")
85}
86
87#[must_use]
89pub fn add_imm(dst: &str, src: &str, imm: i64) -> String {
90 if imm < 0 {
91 format!("sub {dst}, {src}, #{abs}", abs = imm.unsigned_abs())
92 } else {
93 format!("add {dst}, {src}, #{imm}")
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn parse_xreg_accepts_x_and_w_views() {
103 assert_eq!(parse_xreg("x0").as_deref(), Some("x0"));
104 assert_eq!(parse_xreg("w15").as_deref(), Some("w15"));
105 assert_eq!(parse_xreg("x30").as_deref(), Some("x30"));
106 }
107
108 #[test]
109 fn parse_xreg_accepts_zero_registers() {
110 assert_eq!(parse_xreg("xzr").as_deref(), Some("xzr"));
111 assert_eq!(parse_xreg("WZR").as_deref(), Some("wzr"));
112 }
113
114 #[test]
115 fn parse_xreg_strips_trailing_comma_and_whitespace() {
116 assert_eq!(parse_xreg(" x0 , ").as_deref(), Some("x0"));
117 }
118
119 #[test]
120 fn parse_xreg_rejects_stack_pointer() {
121 assert!(parse_xreg("sp").is_none());
122 assert!(parse_xreg("wsp").is_none());
123 }
124
125 #[test]
126 fn parse_xreg_rejects_out_of_range_indices() {
127 assert!(parse_xreg("x31").is_none());
128 assert!(parse_xreg("w99").is_none());
129 }
130
131 #[test]
132 fn parse_xreg_rejects_simd_and_memory_operands() {
133 assert!(parse_xreg("v0").is_none());
134 assert!(parse_xreg("q3").is_none());
135 assert!(parse_xreg("[x0]").is_none());
136 assert!(parse_xreg("#1").is_none());
137 }
138
139 #[test]
140 fn mov_imm_handles_positive_zero_and_negative() {
141 assert_eq!(mov_imm("x0", 0), "mov x0, #0");
142 assert_eq!(mov_imm("x0", 1), "mov x0, #1");
143 assert_eq!(mov_imm("x0", -1), "mov x0, #-1");
144 }
145
146 #[test]
147 fn add_imm_falls_through_to_sub_on_negative() {
148 assert_eq!(add_imm("x0", "x1", 4), "add x0, x1, #4");
149 assert_eq!(add_imm("x0", "x1", -4), "sub x0, x1, #4");
150 }
151
152 #[test]
153 fn register_alias_helpers_emit_canonical_aliases() {
154 assert_eq!(mov_reg("x0", "x1"), "mov x0, x1");
155 assert_eq!(mvn_reg("x0", "x1"), "mvn x0, x1");
156 assert_eq!(neg_reg("x0", "x1"), "neg x0, x1");
157 }
158}