1use rucc_ir::{Def, Extra, Func, Inst, IntPred, Opcode, Type, Value};
34
35use crate::select::Subject;
36
37pub const MAX_ARGS: usize = 3;
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum Shown {
47 Reg,
49 Const,
51 Expand,
53}
54
55pub type Plan = [Shown; MAX_ARGS];
57
58pub const PLAIN: Plan = [Shown::Reg; MAX_ARGS];
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Term {
68 Root,
70 Arg(u8),
72 Deep(u8, u8),
74 Reg(Value),
76 Num(i128),
78}
79
80#[derive(Debug)]
82pub struct Terms<'a> {
83 func: &'a Func,
84 root: Inst,
85 plan: Plan,
86}
87
88impl<'a> Terms<'a> {
89 #[must_use]
91 pub fn new(func: &'a Func, root: Inst, plan: Plan) -> Self {
92 Self { func, root, plan }
93 }
94
95 #[must_use]
97 pub fn root(&self) -> Inst {
98 self.root
99 }
100
101 #[must_use]
104 pub fn name(&self, inst: Inst) -> Option<&'static str> {
105 head_of(self.func, inst)
106 }
107
108 fn args(&self, inst: Inst) -> &[Value] {
110 &self.func[self.func[inst].args]
111 }
112
113 fn arg_value(&self, index: u8) -> Option<Value> {
115 self.args(self.root).get(usize::from(index)).copied()
116 }
117
118 fn def_of(&self, value: Value) -> Option<Inst> {
120 match self.func[value].def {
121 Def::Result { inst, .. } => Some(inst),
122 Def::Param { .. } => None,
123 }
124 }
125
126 #[must_use]
128 pub fn constant(&self, value: Value) -> Option<i128> {
129 let inst = self.def_of(value)?;
130 let data = &self.func[inst];
131 if data.opcode != Opcode::IConst {
132 return None;
133 }
134 let Extra::Imm(imm) = data.extra else { return None };
135 let ty = self.func[value].ty;
136 ty.is_int().then(|| self.func[imm].signed(ty))
137 }
138
139 fn leaf_head(&self, value: Value, shown: Shown) -> Option<(&'static str, usize)> {
142 let ty = self.func[value].ty;
143 let name = match shown {
144 Shown::Reg => value_head(ty)?,
145 Shown::Const => iconst_head(ty)?,
146 Shown::Expand => return None,
148 };
149 Some((name, 1))
150 }
151
152 fn leaf_arg(&self, value: Value, shown: Shown) -> Term {
155 match shown {
156 Shown::Const => self.constant(value).map_or(Term::Reg(value), Term::Num),
157 Shown::Reg | Shown::Expand => Term::Reg(value),
158 }
159 }
160
161 fn deep_shown(&self, value: Value) -> Shown {
168 if self.constant(value).is_some() { Shown::Const } else { Shown::Reg }
169 }
170
171 fn expansion(&self, index: u8) -> Option<(Inst, &[Value])> {
173 let value = self.arg_value(index)?;
174 let inst = self.def_of(value)?;
175 Some((inst, self.args(inst)))
176 }
177}
178
179impl Subject for Terms<'_> {
180 type Node = Term;
181
182 fn head(&self, node: Term) -> Option<(&str, usize)> {
183 match node {
184 Term::Root => {
185 let name = head_of(self.func, self.root)?;
186 let data = &self.func[self.root];
187 let arity =
190 if data.opcode == Opcode::IConst { 1 } else { self.args(self.root).len() };
191 Some((name, arity))
192 }
193 Term::Arg(index) => {
194 let value = self.arg_value(index)?;
195 match self.plan[usize::from(index)] {
196 Shown::Expand => {
197 let (inst, args) = self.expansion(index)?;
198 Some((head_of(self.func, inst)?, args.len()))
199 }
200 shown => self.leaf_head(value, shown),
201 }
202 }
203 Term::Deep(outer, inner) => {
204 let (_, args) = self.expansion(outer)?;
205 let value = *args.get(usize::from(inner))?;
206 self.leaf_head(value, self.deep_shown(value))
207 }
208 Term::Reg(_) | Term::Num(_) => None,
209 }
210 }
211
212 fn arg(&self, node: Term, index: usize) -> Term {
213 let index = u8::try_from(index).unwrap_or(u8::MAX);
214 match node {
215 Term::Root => {
216 let data = &self.func[self.root];
217 if data.opcode == Opcode::IConst {
218 let value = data.first_result.expect("a constant has a result");
219 return self.leaf_arg(value, Shown::Const);
220 }
221 Term::Arg(index)
222 }
223 Term::Arg(outer) => match self.plan[usize::from(outer)] {
224 Shown::Expand => Term::Deep(outer, index),
225 shown => {
226 self.arg_value(outer).map_or(Term::Num(0), |value| self.leaf_arg(value, shown))
227 }
228 },
229 Term::Deep(outer, inner) => {
230 let value = self
231 .expansion(outer)
232 .and_then(|(_, args)| args.get(usize::from(inner)).copied());
233 value.map_or(Term::Num(0), |value| self.leaf_arg(value, self.deep_shown(value)))
234 }
235 Term::Reg(_) | Term::Num(_) => node,
237 }
238 }
239
240 fn int(&self, node: Term) -> Option<i128> {
241 match node {
242 Term::Num(value) => Some(value),
243 _ => None,
244 }
245 }
246}
247
248fn head_of(func: &Func, inst: Inst) -> Option<&'static str> {
255 let data = &func[inst];
256 let result = data.first_result?;
257 let ty = func[result].ty;
258 match data.opcode {
259 Opcode::IConst => iconst_head(ty),
260 Opcode::ICmp => {
261 let Extra::IntPred(pred) = data.extra else { return None };
262 Some(icmp_head(pred))
263 }
264 Opcode::SExt | Opcode::ZExt | Opcode::Trunc => {
265 let from = func[*func[data.args].first()?].ty;
266 convert_head(data.opcode, from, ty)
267 }
268 opcode => binary_head(opcode, ty),
269 }
270}
271
272fn slot(ty: Type) -> Option<usize> {
274 match ty.is_int().then(|| ty.bits())? {
275 8 => Some(0),
276 16 => Some(1),
277 32 => Some(2),
278 64 => Some(3),
279 _ => None,
280 }
281}
282
283fn value_head(ty: Type) -> Option<&'static str> {
285 Some(["value.i8", "value.i16", "value.i32", "value.i64"][slot(ty)?])
286}
287
288fn iconst_head(ty: Type) -> Option<&'static str> {
290 Some(["iconst.i8", "iconst.i16", "iconst.i32", "iconst.i64"][slot(ty)?])
291}
292
293fn icmp_head(pred: IntPred) -> &'static str {
296 match pred {
297 IntPred::Eq => "icmp_eq.i1",
298 IntPred::Ne => "icmp_ne.i1",
299 IntPred::Slt => "icmp_slt.i1",
300 IntPred::Sle => "icmp_sle.i1",
301 IntPred::Sgt => "icmp_sgt.i1",
302 IntPred::Sge => "icmp_sge.i1",
303 IntPred::Ult => "icmp_ult.i1",
304 IntPred::Ule => "icmp_ule.i1",
305 IntPred::Ugt => "icmp_ugt.i1",
306 IntPred::Uge => "icmp_uge.i1",
307 }
308}
309
310fn convert_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
312 let table: &[[Option<&'static str>; 4]; 4] = match opcode {
313 Opcode::SExt => &SEXT,
314 Opcode::ZExt => &ZEXT,
315 Opcode::Trunc => &TRUNC,
316 _ => return None,
317 };
318 table[slot(from)?][slot(to)?]
319}
320
321fn binary_head(opcode: Opcode, ty: Type) -> Option<&'static str> {
323 let names: &[&'static str; 4] = match opcode {
324 Opcode::Add => &["add.i8", "add.i16", "add.i32", "add.i64"],
325 Opcode::Sub => &["sub.i8", "sub.i16", "sub.i32", "sub.i64"],
326 Opcode::Mul => &["mul.i8", "mul.i16", "mul.i32", "mul.i64"],
327 Opcode::SDiv => &["sdiv.i8", "sdiv.i16", "sdiv.i32", "sdiv.i64"],
328 Opcode::UDiv => &["udiv.i8", "udiv.i16", "udiv.i32", "udiv.i64"],
329 Opcode::SRem => &["srem.i8", "srem.i16", "srem.i32", "srem.i64"],
330 Opcode::URem => &["urem.i8", "urem.i16", "urem.i32", "urem.i64"],
331 Opcode::And => &["and.i8", "and.i16", "and.i32", "and.i64"],
332 Opcode::Or => &["or.i8", "or.i16", "or.i32", "or.i64"],
333 Opcode::Xor => &["xor.i8", "xor.i16", "xor.i32", "xor.i64"],
334 Opcode::Shl => &["shl.i8", "shl.i16", "shl.i32", "shl.i64"],
335 Opcode::LShr => &["lshr.i8", "lshr.i16", "lshr.i32", "lshr.i64"],
336 Opcode::AShr => &["ashr.i8", "ashr.i16", "ashr.i32", "ashr.i64"],
337 _ => return None,
338 };
339 Some(names[slot(ty)?])
340}
341
342static SEXT: [[Option<&str>; 4]; 4] = [
346 [None, Some("sext.i8.i16"), Some("sext.i8.i32"), Some("sext.i8.i64")],
347 [None, None, Some("sext.i16.i32"), Some("sext.i16.i64")],
348 [None, None, None, Some("sext.i32.i64")],
349 [None, None, None, None],
350];
351
352static ZEXT: [[Option<&str>; 4]; 4] = [
353 [None, Some("zext.i8.i16"), Some("zext.i8.i32"), Some("zext.i8.i64")],
354 [None, None, Some("zext.i16.i32"), Some("zext.i16.i64")],
355 [None, None, None, Some("zext.i32.i64")],
356 [None, None, None, None],
357];
358
359static TRUNC: [[Option<&str>; 4]; 4] = [
361 [None, None, None, None],
362 [Some("trunc.i16.i8"), None, None, None],
363 [Some("trunc.i32.i8"), Some("trunc.i32.i16"), None, None],
364 [Some("trunc.i64.i8"), Some("trunc.i64.i16"), Some("trunc.i64.i32"), None],
365];
366
367#[cfg(test)]
368mod tests {
369 use rucc_base::Interner;
370 use rucc_ir::{Builder, Flags, Signature};
371
372 use super::*;
373 use crate::select::Subject;
374
375 fn func() -> (Func, rucc_ir::Block) {
377 let mut names = Interner::new();
378 let mut func = Func::new(names.intern("f"), Signature::new());
379 let block = func.create_block();
380 (func, block)
381 }
382
383 fn inst_of(func: &Func, value: Value) -> Inst {
385 match func[value].def {
386 Def::Result { inst, .. } => inst,
387 Def::Param { .. } => unreachable!(),
388 }
389 }
390
391 #[test]
392 fn an_instruction_is_the_term_the_rule_file_names_it_by() {
393 let (mut func, block) = func();
394 let i32 = Type::int(32);
395 let mut build = Builder::new(&mut func, block);
396 let k = build.iconst(i32, 7);
397 let x = build.iconst(i32, 3);
398 let sum = build.binary(Opcode::Add, x, k, Flags::default());
399 let add = inst_of(&func, sum);
400
401 let terms = Terms::new(&func, add, PLAIN);
402 assert_eq!(terms.head(Term::Root), Some(("add.i32", 2)));
403 assert_eq!(terms.head(Term::Arg(0)), Some(("value.i32", 1)));
404 assert_eq!(terms.arg(Term::Arg(0), 0), Term::Reg(x));
405 assert_eq!(terms.head(Term::Reg(x)), None);
406 assert_eq!(terms.int(Term::Reg(x)), None);
407 }
408
409 #[test]
410 fn an_operand_shown_as_a_constant_gives_the_number_up() {
411 let (mut func, block) = func();
412 let i32 = Type::int(32);
413 let mut build = Builder::new(&mut func, block);
414 let x = build.iconst(i32, 3);
415 let k = build.iconst(i32, -7);
416 let sum = build.binary(Opcode::Add, x, k, Flags::default());
417 let add = inst_of(&func, sum);
418
419 let terms = Terms::new(&func, add, [Shown::Reg, Shown::Const, Shown::Reg]);
420 assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i32", 1)));
421 assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(-7));
422 assert_eq!(terms.int(Term::Num(-7)), Some(-7));
423 let plain = Terms::new(&func, add, PLAIN);
426 assert_eq!(plain.head(Term::Arg(1)), Some(("value.i32", 1)));
427 assert_eq!(plain.int(plain.arg(Term::Arg(1), 0)), None);
428 }
429
430 #[test]
431 fn a_constant_is_a_term_of_one_argument_and_has_no_operands() {
432 let (mut func, block) = func();
433 let mut build = Builder::new(&mut func, block);
434 let k = build.iconst(Type::int(64), 12);
435 let inst = inst_of(&func, k);
436
437 let terms = Terms::new(&func, inst, PLAIN);
438 assert_eq!(terms.head(Term::Root), Some(("iconst.i64", 1)));
439 assert_eq!(terms.arg(Term::Root, 0), Term::Num(12));
440 }
441
442 #[test]
443 fn an_expanded_operand_is_the_instruction_that_computed_it() {
444 let (mut func, block) = func();
445 let i64 = Type::int(64);
446 let y = func.append_param(block, i64);
448 let mut build = Builder::new(&mut func, block);
449 let x = build.iconst(i64, 1);
450 let four = build.iconst(i64, 4);
451 let scaled = build.binary(Opcode::Mul, y, four, Flags::default());
452 let sum = build.binary(Opcode::Add, x, scaled, Flags::default());
453 let add = inst_of(&func, sum);
454
455 let terms = Terms::new(&func, add, [Shown::Reg, Shown::Expand, Shown::Reg]);
456 assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
457 assert_eq!(terms.head(Term::Arg(1)), Some(("mul.i64", 2)));
458 assert_eq!(terms.head(Term::Deep(1, 0)), Some(("value.i64", 1)));
459 assert_eq!(terms.arg(Term::Deep(1, 0), 0), Term::Reg(y));
460 assert_eq!(terms.head(Term::Deep(1, 1)), Some(("iconst.i64", 1)));
462 assert_eq!(terms.arg(Term::Deep(1, 1), 0), Term::Num(4));
463 }
464
465 #[test]
466 fn a_comparison_says_which_one_it_is_and_a_conversion_says_both_widths() {
467 let (mut func, block) = func();
468 let mut build = Builder::new(&mut func, block);
469 let x = build.iconst(Type::int(32), 1);
470 let y = build.iconst(Type::int(32), 2);
471 let less = build.icmp(IntPred::Slt, x, y);
472 let wide = build.unary(Opcode::SExt, x, Type::int(64));
473 let narrow = build.unary(Opcode::Trunc, x, Type::int(8));
474 let cmp = inst_of(&func, less);
475 assert_eq!(Terms::new(&func, cmp, PLAIN).head(Term::Root), Some(("icmp_slt.i1", 2)));
476 let sext = inst_of(&func, wide);
477 assert_eq!(Terms::new(&func, sext, PLAIN).head(Term::Root), Some(("sext.i32.i64", 1)));
478 let trunc = inst_of(&func, narrow);
479 assert_eq!(Terms::new(&func, trunc, PLAIN).head(Term::Root), Some(("trunc.i32.i8", 1)));
480 }
481
482 #[test]
483 fn a_width_no_rule_is_written_at_has_no_name() {
484 let (mut func, block) = func();
485 let mut build = Builder::new(&mut func, block);
486 let x = build.iconst(Type::int(128), 1);
487 let inst = inst_of(&func, x);
488 assert_eq!(Terms::new(&func, inst, PLAIN).head(Term::Root), None);
489 }
490}