1pub mod x86_64;
15
16pub use rucc_base::rules::{Guard, Match, Node, Piece, Rule, Subject, Table, Test};
17
18#[cfg(test)]
19mod tests {
20 use super::x86_64::TABLE;
21 use super::{Piece, Subject};
22
23 #[derive(Debug)]
26 enum Node {
27 Int(i128),
28 App(String, Vec<usize>),
29 }
30
31 #[derive(Debug, Default)]
32 struct Terms {
33 nodes: Vec<Node>,
34 }
35
36 impl Terms {
37 fn constant(&mut self, value: i128) -> usize {
38 self.nodes.push(Node::Int(value));
39 self.nodes.len() - 1
40 }
41
42 fn app(&mut self, head: &str, args: &[usize]) -> usize {
43 self.nodes.push(Node::App(head.to_owned(), args.to_vec()));
44 self.nodes.len() - 1
45 }
46
47 fn value(&mut self, width: u32, name: &str) -> usize {
49 let inner = self.app(name, &[]);
50 self.app(&format!("value.i{width}"), &[inner])
51 }
52 }
53
54 impl Subject for Terms {
55 type Node = usize;
56
57 fn head(&self, node: usize) -> Option<(&str, usize)> {
58 match &self.nodes[node] {
59 Node::App(head, args) => Some((head.as_str(), args.len())),
60 Node::Int(_) => None,
61 }
62 }
63
64 fn arg(&self, node: usize, index: usize) -> usize {
65 match &self.nodes[node] {
66 Node::App(_, args) => args[index],
67 Node::Int(_) => unreachable!("a constant has no arguments"),
68 }
69 }
70
71 fn int(&self, node: usize) -> Option<i128> {
72 match self.nodes[node] {
73 Node::Int(value) => Some(value),
74 Node::App(..) => None,
75 }
76 }
77
78 fn same(&self, a: usize, b: usize) -> bool {
81 a == b
82 }
83 }
84
85 fn selects(terms: &Terms, term: usize) -> Option<&'static str> {
88 let found = TABLE.find(terms, term)?;
89 TABLE.rule(&found).head()
90 }
91
92 #[test]
93 fn the_table_holds_every_rule_the_file_writes() {
94 let text = include_str!("../rules/x86-64.rules");
95 let written = text.lines().filter(|line| line.starts_with("(rule ")).count();
96 assert_eq!(TABLE.rules.len(), written, "the table and the rule file disagree");
97 assert_eq!(TABLE.source, "rules/x86-64.rules");
98 }
99
100 #[test]
101 fn an_addition_of_two_registers_is_the_register_form() {
102 let mut terms = Terms::default();
103 let x = terms.value(64, "v0");
104 let y = terms.value(64, "v1");
105 let add = terms.app("add.i64", &[x, y]);
106 assert_eq!(selects(&terms, add), Some("x64.add_rr_64"));
107 }
108
109 #[test]
116 fn a_match_gives_back_the_operands_the_pattern_named() {
117 let mut terms = Terms::default();
118 let first = terms.app("v0", &[]);
119 let second = terms.app("v1", &[]);
120 let x = terms.app("value.i32", &[first]);
121 let y = terms.app("value.i32", &[second]);
122 let sub = terms.app("sub.i32", &[x, y]);
123 let found = TABLE.find(&terms, sub).expect("a rule fires");
124 let rule = TABLE.rule(&found);
125 assert_eq!(rule.pattern, "(sub.i32 (value.i32 x) (value.i32 y))");
126 assert_eq!(found.bindings, vec![first, second]);
127 let names: Vec<&str> = rule
128 .replacement
129 .iter()
130 .filter_map(|piece| match piece {
131 Piece::Var { name, index } => {
132 assert_eq!(found.bindings[*index], if *index == 0 { first } else { second });
133 Some(*name)
134 }
135 _ => None,
136 })
137 .collect();
138 assert_eq!(names, ["x", "y"]);
139 }
140
141 #[test]
144 fn an_addition_of_an_immediate_that_fits_is_the_immediate_form() {
145 let mut terms = Terms::default();
146 let x = terms.value(64, "v0");
147 let k = terms.constant(4);
148 let k = terms.app("iconst.i64", &[k]);
149 let add = terms.app("add.i64", &[x, k]);
150 assert_eq!(selects(&terms, add), Some("x64.add_ri_64"));
151 }
152
153 #[test]
157 fn an_addition_of_an_immediate_too_wide_for_the_form_matches_nothing() {
158 let mut terms = Terms::default();
159 let x = terms.value(64, "v0");
160 let k = terms.constant(1 << 40);
161 let k = terms.app("iconst.i64", &[k]);
162 let add = terms.app("add.i64", &[x, k]);
163 assert_eq!(selects(&terms, add), None);
164 }
165
166 #[test]
168 fn a_shift_by_a_count_the_width_allows_is_the_immediate_form() {
169 let mut terms = Terms::default();
170 let x = terms.value(64, "v0");
171 let k = terms.constant(3);
172 let k = terms.app("iconst.i64", &[k]);
173 let shl = terms.app("shl.i64", &[x, k]);
174 assert_eq!(selects(&terms, shl), Some("x64.shl_ri_64"));
175 }
176
177 #[test]
178 fn a_shift_by_a_count_the_width_does_not_allow_matches_nothing() {
179 let mut terms = Terms::default();
180 let x = terms.value(64, "v0");
181 let k = terms.constant(64);
182 let k = terms.app("iconst.i64", &[k]);
183 let shl = terms.app("shl.i64", &[x, k]);
184 assert_eq!(selects(&terms, shl), None);
185 }
186
187 #[test]
191 fn a_truth_value_is_lowered_to_the_byte_instructions_that_keep_it_one() {
192 let mut terms = Terms::default();
193 let x = terms.value(1, "v0");
194 let y = terms.value(1, "v1");
195 let xor = terms.app("xor.i1", &[x, y]);
196 assert_eq!(selects(&terms, xor), Some("x64.xor_rr_8"));
197
198 let x = terms.value(1, "v2");
199 let wide = terms.app("zext.i1.i32", &[x]);
200 assert_eq!(selects(&terms, wide), Some("x64.bit_to_32"));
201
202 let x = terms.value(8, "v3");
203 let byte = terms.app("zext.i8.i32", &[x]);
204 assert_eq!(selects(&terms, byte), Some("x64.movzx_8_32"));
205 }
206
207 #[test]
210 fn a_term_no_rule_covers_finds_no_rule() {
211 let mut terms = Terms::default();
212 let x = terms.value(64, "v0");
213 let y = terms.value(64, "v1");
214 let odd = terms.app("no.such.opcode", &[x, y]);
215 assert_eq!(selects(&terms, odd), None);
216 }
217}