Skip to main content

rucc_codegen/
term.rs

1//! The IR as something a lowering rule can match against.
2//!
3//! Design: `spec/10-backend.md` section 10.2.
4//!
5//! A rule is written about a term and the compiler has no terms. It has a function full of
6//! instructions, and what a pattern is about is one of them together with whatever its operands
7//! were computed from. So this is the [`Subject`] the matcher asks its three questions of, and
8//! the answers come out of the IR: nothing is built and nothing is thrown away.
9//!
10//! # How an operand is shown
11//!
12//! The same IR value can be several different terms. `(add.i32 (value.i32 x) (iconst.i32 k))`
13//! and `(add.i32 (value.i32 x) (value.i32 y))` are two patterns over one instruction, and which
14//! one it is depends on whether the second operand is a constant and on whether the rule that
15//! wants a constant will take this one. `(add.i64 (value.i64 x) (mul.i64 (value.i64 y)
16//! (iconst.i64 4)))` is a third, and it is about two instructions rather than one.
17//!
18//! The matcher does not backtrack across alternatives for one node: [`Subject::head`] gives one
19//! answer and the walk believes it. So the choice is made before the walk rather than during it.
20//! A [`Plan`] says how each operand of the instruction is shown, the selector tries the plans in
21//! order, and the first that matches is the one that fires. There are at most three ways to show
22//! an operand and at most two operands in any pattern this rule set has, so the whole of the
23//! search is a handful of walks over a trie, each of which fails in its first node or two.
24//!
25//! # How deep it goes
26//!
27//! One level. An operand may be shown as the instruction that computed it, and that
28//! instruction's own operands are shown as a register or as a constant and never expanded
29//! again, which is as deep as any pattern in `x86-64.rules` reaches. A rule set that wants three
30//! levels needs this to grow a level, and it would be found by the rule failing to fire rather
31//! than by anything going wrong.
32
33use rucc_ir::{Def, Extra, FloatPred, Func, Inst, IntPred, Opcode, Type, Value};
34
35use crate::select::Subject;
36
37/// How many operands of one instruction a plan can speak about.
38///
39/// Two is what every pattern in the rule set needs, and a third costs nothing to carry. An
40/// instruction with more operands than this is one no rule matches, which is the same answer it
41/// would get from a plan that could describe it.
42pub const MAX_ARGS: usize = 3;
43
44/// How one operand is shown to the matcher.
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum Shown {
47    /// As a value sitting in a register, which is what `(value.iN x)` matches.
48    Reg,
49    /// As a constant the selector has in hand, which is what `(iconst.iN k)` matches.
50    Const,
51    /// As the instruction that computed it, so a rule can be about two instructions at once.
52    Expand,
53}
54
55/// How every operand of one instruction is shown.
56pub type Plan = [Shown; MAX_ARGS];
57
58/// Everything shown as a register, which is the plan that matches when no other does.
59pub const PLAIN: Plan = [Shown::Reg; MAX_ARGS];
60
61/// One node of the term the matcher is walking.
62///
63/// A position rather than a term, because the term does not exist. Two of these are values in
64/// their own right, and they are the two a pattern can bind: the register a `value` wraps and
65/// the number an `iconst` wraps.
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Term {
68    /// The instruction being selected.
69    Root,
70    /// Operand `i` of the root, shown the way the plan says to show it.
71    Arg(u8),
72    /// Operand `j` of the instruction that computed operand `i` of the root.
73    Deep(u8, u8),
74    /// A value in a register, which is what a pattern binds when it writes `(value.iN x)`.
75    Reg(Value),
76    /// A constant, which is what a pattern binds or tests inside an `(iconst.iN k)`.
77    Num(i128),
78}
79
80/// One instruction of a function, as the terms a rule could match.
81#[derive(Debug)]
82pub struct Terms<'a> {
83    func: &'a Func,
84    root: Inst,
85    plan: Plan,
86}
87
88impl<'a> Terms<'a> {
89    /// The instruction, shown the way the plan says.
90    #[must_use]
91    pub fn new(func: &'a Func, root: Inst, plan: Plan) -> Self {
92        Self { func, root, plan }
93    }
94
95    /// The instruction this is about.
96    #[must_use]
97    pub fn root(&self) -> Inst {
98        self.root
99    }
100
101    /// What the root, or an instruction one of its operands was expanded into, is called in a
102    /// rule file.
103    #[must_use]
104    pub fn name(&self, inst: Inst) -> Option<&'static str> {
105        head_of(self.func, inst)
106    }
107
108    /// The value operands of an instruction.
109    fn args(&self, inst: Inst) -> &[Value] {
110        &self.func[self.func[inst].args]
111    }
112
113    /// Operand `index` of the root, or nothing if it has no such operand.
114    fn arg_value(&self, index: u8) -> Option<Value> {
115        self.args(self.root).get(usize::from(index)).copied()
116    }
117
118    /// The instruction a value is the result of, or nothing for a block parameter.
119    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    /// What a value is, if it is a constant.
127    #[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        if !ty.is_int() {
137            return None;
138        }
139        // One bit is read unsigned, and every other width is read signed. The sign bit of a one
140        // bit integer is the whole of it, so the signed reading of a true is minus one, and what
141        // a rule at that width means by the number it matched is the truth value rather than a
142        // bit pattern. Reading it signed would put a byte of ones in a register where the rest of
143        // the rule set expects a zero or a one.
144        if is_bit(ty) {
145            return Some(i128::try_from(self.func[imm].unsigned()).unwrap_or(0));
146        }
147        Some(self.func[imm].signed(ty))
148    }
149
150    /// The head of a value shown as a register or as a constant, which is a term of one
151    /// argument either way: the thing the pattern binds.
152    fn leaf_head(&self, value: Value, shown: Shown) -> Option<(&'static str, usize)> {
153        let ty = self.func[value].ty;
154        let name = match shown {
155            Shown::Reg => value_head(ty)?,
156            Shown::Const => iconst_head(ty)?,
157            // An expansion is not a leaf, and nothing asks this about one.
158            Shown::Expand => return None,
159        };
160        Some((name, 1))
161    }
162
163    /// What a value shown as a register or as a constant binds, which is the value itself or
164    /// the number it is.
165    fn leaf_arg(&self, value: Value, shown: Shown) -> Term {
166        match shown {
167            Shown::Const => self.constant(value).map_or(Term::Reg(value), Term::Num),
168            Shown::Reg | Shown::Expand => Term::Reg(value),
169        }
170    }
171
172    /// How an operand of an expanded operand is shown, which is as a constant when it is one
173    /// and as a register otherwise.
174    ///
175    /// There is no choice to make here. The reason to show a constant as a register is that no
176    /// rule would take it as an immediate, and the answer to that inside an expansion is to
177    /// stop expanding, which is a plan the selector tries anyway.
178    fn deep_shown(&self, value: Value) -> Shown {
179        if self.constant(value).is_some() { Shown::Const } else { Shown::Reg }
180    }
181
182    /// The instruction an expanded operand of the root was computed by, with its operands.
183    fn expansion(&self, index: u8) -> Option<(Inst, &[Value])> {
184        let value = self.arg_value(index)?;
185        let inst = self.def_of(value)?;
186        Some((inst, self.args(inst)))
187    }
188}
189
190impl Subject for Terms<'_> {
191    type Node = Term;
192
193    fn head(&self, node: Term) -> Option<(&str, usize)> {
194        match node {
195            Term::Root => {
196                let name = head_of(self.func, self.root)?;
197                let data = &self.func[self.root];
198                // A constant has no operands and its term has one, which is the constant, so it
199                // is the one instruction whose arity is not the length of its operand list.
200                let arity =
201                    if data.opcode == Opcode::IConst { 1 } else { self.args(self.root).len() };
202                Some((name, arity))
203            }
204            Term::Arg(index) => {
205                let value = self.arg_value(index)?;
206                match self.plan[usize::from(index)] {
207                    Shown::Expand => {
208                        let (inst, args) = self.expansion(index)?;
209                        Some((head_of(self.func, inst)?, args.len()))
210                    }
211                    shown => self.leaf_head(value, shown),
212                }
213            }
214            Term::Deep(outer, inner) => {
215                let (_, args) = self.expansion(outer)?;
216                let value = *args.get(usize::from(inner))?;
217                self.leaf_head(value, self.deep_shown(value))
218            }
219            Term::Reg(_) | Term::Num(_) => None,
220        }
221    }
222
223    fn arg(&self, node: Term, index: usize) -> Term {
224        let index = u8::try_from(index).unwrap_or(u8::MAX);
225        match node {
226            Term::Root => {
227                let data = &self.func[self.root];
228                if data.opcode == Opcode::IConst {
229                    let value = data.first_result.expect("a constant has a result");
230                    return self.leaf_arg(value, Shown::Const);
231                }
232                Term::Arg(index)
233            }
234            Term::Arg(outer) => match self.plan[usize::from(outer)] {
235                Shown::Expand => Term::Deep(outer, index),
236                shown => {
237                    self.arg_value(outer).map_or(Term::Num(0), |value| self.leaf_arg(value, shown))
238                }
239            },
240            Term::Deep(outer, inner) => {
241                let value = self
242                    .expansion(outer)
243                    .and_then(|(_, args)| args.get(usize::from(inner)).copied());
244                value.map_or(Term::Num(0), |value| self.leaf_arg(value, self.deep_shown(value)))
245            }
246            // Neither has a head, so nothing asks either of them for an argument.
247            Term::Reg(_) | Term::Num(_) => node,
248        }
249    }
250
251    fn int(&self, node: Term) -> Option<i128> {
252        match node {
253            Term::Num(value) => Some(value),
254            _ => None,
255        }
256    }
257}
258
259/// What an instruction is called in a rule file, or nothing if the rules have no name for it.
260///
261/// The name carries the width, because a rule file that did not say how wide a term is would be
262/// a file whose reader has to look at the line above to find out. Which widths there are names
263/// for is the rule language's business and not this crate's: an instruction at a width nothing
264/// is written about has no name here, and the answer to it is that no rule matches.
265fn head_of(func: &Func, inst: Inst) -> Option<&'static str> {
266    let data = &func[inst];
267
268    // A store is the one instruction with a name here that computes nothing, so the width in
269    // its name is the width of what it is storing and has to come from an operand. That operand
270    // is the first one, which is the order `rucc_ir::Builder::store` puts them in and the order
271    // a pattern for one is written in.
272    //
273    // Nothing looks at the flags or the ordering, and both of those are worth saying out loud.
274    // A `volatile` access has to happen exactly once and must not move, and neither of those is
275    // something selection does: one IR load is one instruction whatever its flags say, and
276    // folding the address arithmetic into the addressing mode does not change how many times
277    // memory is touched. An ordering would be a different matter, because a store that releases
278    // is not a plain `mov` on any machine where it means anything, but an ordered access is
279    // `atomic_load` or `atomic_store` and those are different opcodes with no name here. The IR
280    // verifier is what makes that true rather than merely usual: it rejects an ordering on a
281    // plain access, so by the time anything is selected there is none to miss.
282    if data.opcode == Opcode::Store {
283        let value = *func[data.args].first()?;
284        return store_head(func[value].ty);
285    }
286
287    // A return is the other one, and the width comes from the operand for the same reason. A
288    // return of nothing has no name, and neither has a return of more than one value: a rule
289    // for either would have to say where each of them goes, and where a value goes is a fact
290    // about the convention rather than about a term, so the rule language has nothing to say
291    // about it. A return of nothing needs no rule at all, since the epilogue is the whole of it.
292    if data.opcode == Opcode::Return {
293        let [value] = &func[data.args] else { return None };
294        return ret_head(func[*value].ty);
295    }
296
297    // A conditional branch is the third instruction here that computes nothing. Where it goes is
298    // not part of its name and not part of any pattern: a machine IR block holds its own
299    // successors, so a rule for a branch never has to say a block, and what is left for it to say
300    // is what the branch is about, which is the condition.
301    if data.opcode == Opcode::BrIf {
302        let [cond] = &func[data.args] else { return None };
303        return (func[*cond].ty == Type::int(1)).then_some("brif.i1");
304    }
305
306    let result = data.first_result?;
307    let ty = func[result].ty;
308    match data.opcode {
309        Opcode::IConst => iconst_head(ty),
310        Opcode::Load => load_head(ty),
311        Opcode::ICmp => {
312            let Extra::IntPred(pred) = data.extra else { return None };
313            Some(icmp_head(pred))
314        }
315        // A float comparison, whose name comes from the operands rather than from the result: the
316        // result is one bit either way and what tells the two instructions apart is the format.
317        Opcode::FCmp => {
318            let Extra::FloatPred(pred) = data.extra else { return None };
319            fcmp_head(pred, func[*func[data.args].first()?].ty)
320        }
321        Opcode::SExt | Opcode::ZExt | Opcode::Trunc => {
322            let from = func[*func[data.args].first()?].ty;
323            convert_head(data.opcode, from, ty)
324        }
325        // The conversions with a float on one side or both. A separate row because what is on
326        // each side is part of the name and a width alone would not say which register file the
327        // value is in, which is the whole difference between these and the three above.
328        Opcode::FPExt | Opcode::FPTrunc | Opcode::FPToSI | Opcode::SIToFP | Opcode::Bitcast => {
329            let from = func[*func[data.args].first()?].ty;
330            cross_head(data.opcode, from, ty)
331        }
332        // Address arithmetic is an add at the address width, which is all it is once both
333        // operands are in registers: the offset is already in bytes, which the IR guarantees and
334        // the front end is what did the multiplying. Calling it that is what lets every rule
335        // written about an add reach it, including the ones that fold it into an addressing mode,
336        // and there is nothing in any of them it could get wrong.
337        Opcode::PtrAdd => binary_head(Opcode::Add, ty),
338        opcode => binary_head(opcode, ty),
339    }
340}
341
342/// How wide an address is on the machine this lowers for.
343///
344/// The rule set has no term for a pointer and needs none. An address in a register is an integer
345/// of the machine's address width, every rule that could compute one is a rule about an integer
346/// of that width, and the only thing missing was a name. [`slot`] used to ask the type how wide
347/// it was, and a pointer answers nothing, because how wide an address is belongs to the target
348/// rather than to the IR. So this is where the target's answer is written down.
349///
350/// Sixty four, and it is a constant for the same reason the `x64.` prefix and the table in
351/// [`crate::select::x86_64`] are: this crate lowers for one machine. Every architecture
352/// `rucc_target::Arch` names is a sixty four bit one, so there is no target in the compiler that
353/// would want a different number, and a thirty two bit one would want more from this crate than
354/// a number.
355const ADDRESS: u32 = 64;
356
357/// Which of the four widths a type is, or nothing for a width no rule is written at.
358///
359/// A pointer is one of them, at [`ADDRESS`]. A vector is none of them however wide its lane is,
360/// because a rule at a width says nothing about how many lanes it acts on and lowering an add of
361/// four lanes to an add of one would be wrong rather than incomplete.
362pub(crate) fn slot(ty: Type) -> Option<usize> {
363    if !ty.is_scalar() {
364        return None;
365    }
366    let bits = if ty.is_ptr() { ADDRESS } else { ty.is_int().then(|| ty.bits())? };
367    match bits {
368        8 => Some(0),
369        16 => Some(1),
370        32 => Some(2),
371        64 => Some(3),
372        _ => None,
373    }
374}
375
376/// Which of the two float widths a type is, or nothing for anything that is not a float.
377///
378/// Two rather than [`slot`]'s four, and a table of its own rather than more entries in that one,
379/// because a `float` and an `int` of the same width are not the same term to any rule: they are in
380/// different register files and every instruction that touches them is a different instruction. A
381/// `long double` is none of them, since it is on the x87 stack rather than in a vector register
382/// and nothing here is written about that stack.
383pub(crate) fn float_slot(ty: Type) -> Option<usize> {
384    if !ty.is_scalar() || !ty.is_float() {
385        return None;
386    }
387    match ty.bits() {
388        32 => Some(0),
389        64 => Some(1),
390        _ => None,
391    }
392}
393
394/// Whether a type is the one bit a truth value comes in.
395///
396/// One bit is a width the rule set is written at and is not one of [`slot`]'s four, because it is
397/// not a width the machine computes in. There is no one bit register and no one bit instruction: a
398/// value of this width lives in a whole byte with the other seven bits zero, which is what a
399/// `setcc` leaves behind, and every rule written at one bit is a byte instruction chosen because
400/// it keeps that true. The model says the same thing from the other side, giving `setcc` a meaning
401/// one bit wide, so the abstraction is stated in both places rather than assumed in either.
402///
403/// What makes the invariant hold rather than merely be usual is that nothing else at this width
404/// has a name. A comparison is the only instruction that produces one, the bitwise operations
405/// below carry it through unchanged, and everything else at one bit reaches [`slot`] and gets
406/// nothing, so there is no rule that could put a byte here which is not a zero or a one.
407fn is_bit(ty: Type) -> bool {
408    ty.is_scalar() && ty.is_int() && ty.bits() == 1
409}
410
411/// What a value in a register is called at that width.
412fn value_head(ty: Type) -> Option<&'static str> {
413    if is_bit(ty) {
414        return Some("value.i1");
415    }
416    if let Some(at) = float_slot(ty) {
417        return Some(["value.f32", "value.f64"][at]);
418    }
419    Some(["value.i8", "value.i16", "value.i32", "value.i64"][slot(ty)?])
420}
421
422/// What a constant is called at that width.
423///
424/// An integer and not an address, unlike everything else here. What a pattern binds inside one of
425/// these is the number, and [`Terms::constant`] only has a number for an integer, so a term that
426/// named an address would be one a rule could match and then find nothing behind.
427fn iconst_head(ty: Type) -> Option<&'static str> {
428    if !ty.is_int() {
429        return None;
430    }
431    if is_bit(ty) {
432        return Some("iconst.i1");
433    }
434    Some(["iconst.i8", "iconst.i16", "iconst.i32", "iconst.i64"][slot(ty)?])
435}
436
437/// What a load is called, which is the width of the value it produced.
438fn load_head(ty: Type) -> Option<&'static str> {
439    if let Some(at) = float_slot(ty) {
440        return Some(["load.f32", "load.f64"][at]);
441    }
442    Some(["load.i8", "load.i16", "load.i32", "load.i64"][slot(ty)?])
443}
444
445/// What a store is called, which is the width of the value it writes, since it produces nothing
446/// to take a width from.
447fn store_head(ty: Type) -> Option<&'static str> {
448    if let Some(at) = float_slot(ty) {
449        return Some(["store.f32", "store.f64"][at]);
450    }
451    Some(["store.i8", "store.i16", "store.i32", "store.i64"][slot(ty)?])
452}
453
454/// What a return is called, which is the width of the value it gives back, for the same reason.
455fn ret_head(ty: Type) -> Option<&'static str> {
456    if let Some(at) = float_slot(ty) {
457        return Some(["ret.f32", "ret.f64"][at]);
458    }
459    Some(["ret.i8", "ret.i16", "ret.i32", "ret.i64"][slot(ty)?])
460}
461
462/// What a comparison is called, which does not carry the width of what it compared: the result
463/// is one bit whatever the operands were, and the operands say how wide they are themselves.
464fn icmp_head(pred: IntPred) -> &'static str {
465    match pred {
466        IntPred::Eq => "icmp_eq.i1",
467        IntPred::Ne => "icmp_ne.i1",
468        IntPred::Slt => "icmp_slt.i1",
469        IntPred::Sle => "icmp_sle.i1",
470        IntPred::Sgt => "icmp_sgt.i1",
471        IntPred::Sge => "icmp_sge.i1",
472        IntPred::Ult => "icmp_ult.i1",
473        IntPred::Ule => "icmp_ule.i1",
474        IntPred::Ugt => "icmp_ugt.i1",
475        IntPred::Uge => "icmp_uge.i1",
476    }
477}
478
479/// What a float comparison is called, which does carry the format of what it compared.
480///
481/// The difference from [`icmp_head`] is the whole reason this is a second function. A comparison
482/// of two integers is the same instruction whatever file they came from, because there is only one
483/// file they could have come from, so the width lives on the operands and the name says nothing
484/// about it. A comparison of two floats is a different instruction for a `float` and a `double`,
485/// and the operands are in registers that hold either, so the name has to say which.
486///
487/// The two predicates that read nothing have no name here. `false` and `true` do not look at their
488/// operands, so a rule for either would be a rule that computes a constant out of a comparison it
489/// did not make, and the front end writes neither: nothing in C spells them and nothing here folds
490/// a comparison into one yet.
491fn fcmp_head(pred: FloatPred, ty: Type) -> Option<&'static str> {
492    let at = float_slot(ty)?;
493    let names: [&'static str; 2] = match pred {
494        FloatPred::Oeq => ["fcmp_oeq.f32.i1", "fcmp_oeq.f64.i1"],
495        FloatPred::Ogt => ["fcmp_ogt.f32.i1", "fcmp_ogt.f64.i1"],
496        FloatPred::Oge => ["fcmp_oge.f32.i1", "fcmp_oge.f64.i1"],
497        FloatPred::Olt => ["fcmp_olt.f32.i1", "fcmp_olt.f64.i1"],
498        FloatPred::Ole => ["fcmp_ole.f32.i1", "fcmp_ole.f64.i1"],
499        FloatPred::One => ["fcmp_one.f32.i1", "fcmp_one.f64.i1"],
500        FloatPred::Ord => ["fcmp_ord.f32.i1", "fcmp_ord.f64.i1"],
501        FloatPred::Uno => ["fcmp_uno.f32.i1", "fcmp_uno.f64.i1"],
502        FloatPred::Ueq => ["fcmp_ueq.f32.i1", "fcmp_ueq.f64.i1"],
503        FloatPred::Ugt => ["fcmp_ugt.f32.i1", "fcmp_ugt.f64.i1"],
504        FloatPred::Uge => ["fcmp_uge.f32.i1", "fcmp_uge.f64.i1"],
505        FloatPred::Ult => ["fcmp_ult.f32.i1", "fcmp_ult.f64.i1"],
506        FloatPred::Ule => ["fcmp_ule.f32.i1", "fcmp_ule.f64.i1"],
507        FloatPred::Une => ["fcmp_une.f32.i1", "fcmp_une.f64.i1"],
508        FloatPred::False | FloatPred::True => return None,
509    };
510    Some(names[at])
511}
512
513/// What a conversion is called, which is the two widths it is between.
514///
515/// A widening from one bit is the one conversion this width has, and it is a row of its own rather
516/// than a fifth entry in the tables below. A five by five table would have a name for every
517/// conversion between one bit and every other width in both directions, and all but four of those
518/// are conversions nothing writes: a narrowing to one bit is a comparison against zero, which is a
519/// different opcode, and a sign extension from one bit is what an `unsigned` comparison result
520/// would need and there is none.
521fn convert_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
522    if is_bit(from) {
523        if opcode != Opcode::ZExt {
524            return None;
525        }
526        return Some(["zext.i1.i8", "zext.i1.i16", "zext.i1.i32", "zext.i1.i64"][slot(to)?]);
527    }
528    let table: &[[Option<&'static str>; 4]; 4] = match opcode {
529        Opcode::SExt => &SEXT,
530        Opcode::ZExt => &ZEXT,
531        Opcode::Trunc => &TRUNC,
532        _ => return None,
533    };
534    table[slot(from)?][slot(to)?]
535}
536
537/// Which of the two integer widths a conversion to or from a float is written at, or nothing for
538/// any other width.
539///
540/// The machine converts at thirty two bits and at sixty four and at no width below them. A C
541/// program turning a `double` into a `short` is a conversion to `int` and a truncation after it,
542/// and the front end is what writes the truncation, so a narrower conversion arriving here has no
543/// name and is reported rather than lowered to an instruction that would round it in the wrong
544/// place.
545fn cross_slot(ty: Type) -> Option<usize> {
546    match slot(ty)? {
547        2 => Some(0),
548        3 => Some(1),
549        _ => None,
550    }
551}
552
553/// Whether that type is the integer the float at that index shares its width with.
554///
555/// A pointer is not, however wide it is. The IR has `ptrtoint` for turning an address into a
556/// number, and a `bitcast` that moved one through a vector register would be hiding that
557/// conversion rather than performing it, which is what the IR verifier says as well.
558fn paired_int(ty: Type, at: usize) -> bool {
559    ty.is_scalar() && ty.is_int() && ty.bits() == [32, 64][at]
560}
561
562/// What a conversion with a float on one side or both is called, which is what it goes between and
563/// which side each of them is on.
564///
565/// The name carries the format where an integer conversion carries a width, for the reason
566/// [`float_slot`] gives: a `float` and an `int` of the same width are in different register files
567/// and no rule written about one says anything about the other. So there is no name here that
568/// could be read as either, and a rule for `fptosi.f64.i32` cannot match anything but a `double`
569/// becoming an `int`.
570///
571/// The unsigned conversions have no name. The machine has no instruction for either below a
572/// register wider than anything this allocates, so each is several instructions and belongs in a
573/// pass that rewrites it into these rather than in a rule that would have to be several
574/// instructions long.
575fn cross_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
576    match opcode {
577        // Between the two formats, one name each way. There is no third format with a name here,
578        // so these two are the whole of it rather than the first two of a table.
579        Opcode::FPExt => {
580            (float_slot(from)? == 0 && float_slot(to)? == 1).then_some("fpext.f32.f64")
581        }
582        Opcode::FPTrunc => {
583            (float_slot(from)? == 1 && float_slot(to)? == 0).then_some("fptrunc.f64.f32")
584        }
585        Opcode::FPToSI => Some(FPTOSI[float_slot(from)?][cross_slot(to)?]),
586        Opcode::SIToFP => Some(SITOFP[cross_slot(from)?][float_slot(to)?]),
587        // A reinterpretation, which is a `movd` or a `movq` between the two register files and is
588        // the one conversion here that changes no bit. Between two integers or between two floats
589        // it is nothing at all, since the IR keeps the width the same, so the four that cross the
590        // files are the four with a name.
591        Opcode::Bitcast => match (float_slot(from), float_slot(to)) {
592            (Some(at), None) if paired_int(to, at) => {
593                Some(["bitcast.f32.i32", "bitcast.f64.i64"][at])
594            }
595            (None, Some(at)) if paired_int(from, at) => {
596                Some(["bitcast.i32.f32", "bitcast.i64.f64"][at])
597            }
598            _ => None,
599        },
600        _ => None,
601    }
602}
603
604/// A float to a signed integer, from the format down the side to the width across the top.
605static FPTOSI: [[&str; 2]; 2] =
606    [["fptosi.f32.i32", "fptosi.f32.i64"], ["fptosi.f64.i32", "fptosi.f64.i64"]];
607
608/// A signed integer to a float, the other way round.
609static SITOFP: [[&str; 2]; 2] =
610    [["sitofp.i32.f32", "sitofp.i32.f64"], ["sitofp.i64.f32", "sitofp.i64.f64"]];
611
612/// What each of the binary operations is called at each width.
613///
614/// The three bitwise ones are the only ones with a name at one bit. They are what a `!=` between
615/// two truth values and a `&&` folded to one instruction become, and each of them takes two bytes
616/// that are a zero or a one to a byte that is a zero or a one. There is nothing to be gained by an
617/// add or a shift at this width and no front end writes one.
618fn binary_head(opcode: Opcode, ty: Type) -> Option<&'static str> {
619    if is_bit(ty) {
620        return match opcode {
621            Opcode::And => Some("and.i1"),
622            Opcode::Or => Some("or.i1"),
623            Opcode::Xor => Some("xor.i1"),
624            _ => None,
625        };
626    }
627    if let Some(at) = float_slot(ty) {
628        // The four the machine has one instruction each for. A remainder is not among them: there
629        // is no scalar instruction for it and what C means by `fmod` is a call, so an `frem` that
630        // reached here would find no rule and be reported rather than lowered to something else.
631        let names: &[&'static str; 2] = match opcode {
632            Opcode::FAdd => &["fadd.f32", "fadd.f64"],
633            Opcode::FSub => &["fsub.f32", "fsub.f64"],
634            Opcode::FMul => &["fmul.f32", "fmul.f64"],
635            Opcode::FDiv => &["fdiv.f32", "fdiv.f64"],
636            _ => return None,
637        };
638        return Some(names[at]);
639    }
640    let names: &[&'static str; 4] = match opcode {
641        Opcode::Add => &["add.i8", "add.i16", "add.i32", "add.i64"],
642        Opcode::Sub => &["sub.i8", "sub.i16", "sub.i32", "sub.i64"],
643        Opcode::Mul => &["mul.i8", "mul.i16", "mul.i32", "mul.i64"],
644        Opcode::SDiv => &["sdiv.i8", "sdiv.i16", "sdiv.i32", "sdiv.i64"],
645        Opcode::UDiv => &["udiv.i8", "udiv.i16", "udiv.i32", "udiv.i64"],
646        Opcode::SRem => &["srem.i8", "srem.i16", "srem.i32", "srem.i64"],
647        Opcode::URem => &["urem.i8", "urem.i16", "urem.i32", "urem.i64"],
648        Opcode::And => &["and.i8", "and.i16", "and.i32", "and.i64"],
649        Opcode::Or => &["or.i8", "or.i16", "or.i32", "or.i64"],
650        Opcode::Xor => &["xor.i8", "xor.i16", "xor.i32", "xor.i64"],
651        Opcode::Shl => &["shl.i8", "shl.i16", "shl.i32", "shl.i64"],
652        Opcode::LShr => &["lshr.i8", "lshr.i16", "lshr.i32", "lshr.i64"],
653        Opcode::AShr => &["ashr.i8", "ashr.i16", "ashr.i32", "ashr.i64"],
654        _ => return None,
655    };
656    Some(names[slot(ty)?])
657}
658
659/// The widening conversions, from the width down the side to the width across the top. The
660/// diagonal and everything below it is empty, because a sign extension to a width it already
661/// has is not an instruction and the IR does not have one.
662static SEXT: [[Option<&str>; 4]; 4] = [
663    [None, Some("sext.i8.i16"), Some("sext.i8.i32"), Some("sext.i8.i64")],
664    [None, None, Some("sext.i16.i32"), Some("sext.i16.i64")],
665    [None, None, None, Some("sext.i32.i64")],
666    [None, None, None, None],
667];
668
669static ZEXT: [[Option<&str>; 4]; 4] = [
670    [None, Some("zext.i8.i16"), Some("zext.i8.i32"), Some("zext.i8.i64")],
671    [None, None, Some("zext.i16.i32"), Some("zext.i16.i64")],
672    [None, None, None, Some("zext.i32.i64")],
673    [None, None, None, None],
674];
675
676/// The narrowing ones, which fill the other corner for the same reason.
677static TRUNC: [[Option<&str>; 4]; 4] = [
678    [None, None, None, None],
679    [Some("trunc.i16.i8"), None, None, None],
680    [Some("trunc.i32.i8"), Some("trunc.i32.i16"), None, None],
681    [Some("trunc.i64.i8"), Some("trunc.i64.i16"), Some("trunc.i64.i32"), None],
682];
683
684#[cfg(test)]
685mod tests {
686    use rucc_base::Interner;
687    use rucc_ir::{Builder, Flags, Float, Signature};
688
689    use super::*;
690    use crate::select::Subject;
691
692    /// A function with one block, and the builder to put instructions in it.
693    fn func() -> (Func, rucc_ir::Block) {
694        let mut names = Interner::new();
695        let mut func = Func::new(names.intern("f"), Signature::new());
696        let block = func.create_block();
697        (func, block)
698    }
699
700    /// The instruction that computed a value, which every value in these tests has.
701    fn inst_of(func: &Func, value: Value) -> Inst {
702        match func[value].def {
703            Def::Result { inst, .. } => inst,
704            Def::Param { .. } => unreachable!(),
705        }
706    }
707
708    #[test]
709    fn an_instruction_is_the_term_the_rule_file_names_it_by() {
710        let (mut func, block) = func();
711        let i32 = Type::int(32);
712        let mut build = Builder::new(&mut func, block);
713        let k = build.iconst(i32, 7);
714        let x = build.iconst(i32, 3);
715        let sum = build.binary(Opcode::Add, x, k, Flags::default());
716        let add = inst_of(&func, sum);
717
718        let terms = Terms::new(&func, add, PLAIN);
719        assert_eq!(terms.head(Term::Root), Some(("add.i32", 2)));
720        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i32", 1)));
721        assert_eq!(terms.arg(Term::Arg(0), 0), Term::Reg(x));
722        assert_eq!(terms.head(Term::Reg(x)), None);
723        assert_eq!(terms.int(Term::Reg(x)), None);
724    }
725
726    #[test]
727    fn an_operand_shown_as_a_constant_gives_the_number_up() {
728        let (mut func, block) = func();
729        let i32 = Type::int(32);
730        let mut build = Builder::new(&mut func, block);
731        let x = build.iconst(i32, 3);
732        let k = build.iconst(i32, -7);
733        let sum = build.binary(Opcode::Add, x, k, Flags::default());
734        let add = inst_of(&func, sum);
735
736        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Const, Shown::Reg]);
737        assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i32", 1)));
738        assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(-7));
739        assert_eq!(terms.int(Term::Num(-7)), Some(-7));
740        // The same operand shown as a register is a register, and a guard asking what number it
741        // is gets no answer, which is what makes a rule about a number decline it.
742        let plain = Terms::new(&func, add, PLAIN);
743        assert_eq!(plain.head(Term::Arg(1)), Some(("value.i32", 1)));
744        assert_eq!(plain.int(plain.arg(Term::Arg(1), 0)), None);
745    }
746
747    #[test]
748    fn a_constant_is_a_term_of_one_argument_and_has_no_operands() {
749        let (mut func, block) = func();
750        let mut build = Builder::new(&mut func, block);
751        let k = build.iconst(Type::int(64), 12);
752        let inst = inst_of(&func, k);
753
754        let terms = Terms::new(&func, inst, PLAIN);
755        assert_eq!(terms.head(Term::Root), Some(("iconst.i64", 1)));
756        assert_eq!(terms.arg(Term::Root, 0), Term::Num(12));
757    }
758
759    #[test]
760    fn an_expanded_operand_is_the_instruction_that_computed_it() {
761        let (mut func, block) = func();
762        let i64 = Type::int(64);
763        // A parameter, because the point of the test is an operand that is not a constant.
764        let y = func.append_param(block, i64);
765        let mut build = Builder::new(&mut func, block);
766        let x = build.iconst(i64, 1);
767        let four = build.iconst(i64, 4);
768        let scaled = build.binary(Opcode::Mul, y, four, Flags::default());
769        let sum = build.binary(Opcode::Add, x, scaled, Flags::default());
770        let add = inst_of(&func, sum);
771
772        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Expand, Shown::Reg]);
773        assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
774        assert_eq!(terms.head(Term::Arg(1)), Some(("mul.i64", 2)));
775        assert_eq!(terms.head(Term::Deep(1, 0)), Some(("value.i64", 1)));
776        assert_eq!(terms.arg(Term::Deep(1, 0), 0), Term::Reg(y));
777        // The constant inside an expansion is shown as one without being asked to be.
778        assert_eq!(terms.head(Term::Deep(1, 1)), Some(("iconst.i64", 1)));
779        assert_eq!(terms.arg(Term::Deep(1, 1), 0), Term::Num(4));
780    }
781
782    #[test]
783    fn a_comparison_says_which_one_it_is_and_a_conversion_says_both_widths() {
784        let (mut func, block) = func();
785        let mut build = Builder::new(&mut func, block);
786        let x = build.iconst(Type::int(32), 1);
787        let y = build.iconst(Type::int(32), 2);
788        let less = build.icmp(IntPred::Slt, x, y);
789        let wide = build.unary(Opcode::SExt, x, Type::int(64));
790        let narrow = build.unary(Opcode::Trunc, x, Type::int(8));
791        let cmp = inst_of(&func, less);
792        assert_eq!(Terms::new(&func, cmp, PLAIN).head(Term::Root), Some(("icmp_slt.i1", 2)));
793        let sext = inst_of(&func, wide);
794        assert_eq!(Terms::new(&func, sext, PLAIN).head(Term::Root), Some(("sext.i32.i64", 1)));
795        let trunc = inst_of(&func, narrow);
796        assert_eq!(Terms::new(&func, trunc, PLAIN).head(Term::Root), Some(("trunc.i32.i8", 1)));
797    }
798
799    #[test]
800    fn a_width_no_rule_is_written_at_has_no_name() {
801        let (mut func, block) = func();
802        let mut build = Builder::new(&mut func, block);
803        let x = build.iconst(Type::int(128), 1);
804        let inst = inst_of(&func, x);
805        assert_eq!(Terms::new(&func, inst, PLAIN).head(Term::Root), None);
806    }
807
808    /// An address is an integer of the machine's width to every term here, which is what lets one
809    /// be loaded from, stored through, returned and added to by rules written about integers.
810    #[test]
811    fn an_address_is_an_integer_as_wide_as_the_machine_addresses() {
812        assert_eq!(value_head(Type::PTR), Some("value.i64"));
813        assert_eq!(load_head(Type::PTR), Some("load.i64"));
814        assert_eq!(store_head(Type::PTR), Some("store.i64"));
815        assert_eq!(ret_head(Type::PTR), Some("ret.i64"));
816        // Not a constant, since nothing writes an address down as one.
817        assert_eq!(iconst_head(Type::PTR), None);
818    }
819
820    /// One bit is a width with names of its own, and they are not the four the tables hold. What
821    /// has a name there is what a truth value is written with: a constant, the three bitwise
822    /// operations, and the widening that turns one into a number.
823    #[test]
824    fn one_bit_is_a_width_with_a_name_for_what_a_truth_value_is_written_with() {
825        let bit = Type::int(1);
826        assert_eq!(slot(bit), None);
827        assert_eq!(value_head(bit), Some("value.i1"));
828        assert_eq!(iconst_head(bit), Some("iconst.i1"));
829        assert_eq!(binary_head(Opcode::And, bit), Some("and.i1"));
830        assert_eq!(binary_head(Opcode::Or, bit), Some("or.i1"));
831        assert_eq!(binary_head(Opcode::Xor, bit), Some("xor.i1"));
832        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(8)), Some("zext.i1.i8"));
833        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(32)), Some("zext.i1.i32"));
834        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(64)), Some("zext.i1.i64"));
835    }
836
837    /// Everything else at one bit has no name, which is what keeps the byte holding one a zero or
838    /// a one: an add at this width would be an instruction that leaves something else there.
839    #[test]
840    fn nothing_else_at_one_bit_has_a_name() {
841        let bit = Type::int(1);
842        assert_eq!(binary_head(Opcode::Add, bit), None);
843        assert_eq!(binary_head(Opcode::Shl, bit), None);
844        assert_eq!(load_head(bit), None);
845        assert_eq!(store_head(bit), None);
846        assert_eq!(ret_head(bit), None);
847        // Not a sign extension either, which would be a truth value spread over every bit.
848        assert_eq!(convert_head(Opcode::SExt, bit, Type::int(32)), None);
849        // And not a narrowing to it, since what makes a number into a truth value is a
850        // comparison against zero and that is a different opcode.
851        assert_eq!(convert_head(Opcode::Trunc, Type::int(32), bit), None);
852    }
853
854    /// A one bit constant is the truth value it stands for. The signed reading of a one bit
855    /// integer turns a true into a minus one, which would put a byte of ones where every rule at
856    /// this width expects a one.
857    #[test]
858    fn a_one_bit_constant_is_a_zero_or_a_one_rather_than_a_zero_or_a_minus_one() {
859        let (mut func, block) = func();
860        let mut build = Builder::new(&mut func, block);
861        let bit = Type::int(1);
862        let no = build.iconst(bit, 0);
863        let yes = build.iconst(bit, 1);
864        let terms = Terms::new(&func, inst_of(&func, yes), PLAIN);
865        assert_eq!(terms.constant(no), Some(0));
866        assert_eq!(terms.constant(yes), Some(1));
867        assert_eq!(terms.head(Term::Root), Some(("iconst.i1", 1)));
868        assert_eq!(terms.arg(Term::Root, 0), Term::Num(1));
869    }
870
871    /// A float is a term of its own at each of the two widths the machine has instructions for.
872    /// The same width of integer is a different term, which is what keeps a rule about one from
873    /// ever firing on the other, and it has to be, because the two are in different register
874    /// files.
875    #[test]
876    fn a_float_is_a_term_of_its_own_at_each_width_the_machine_computes_in() {
877        let f32 = Type::float(Float::F32);
878        let f64 = Type::float(Float::F64);
879        assert_eq!(value_head(f32), Some("value.f32"));
880        assert_eq!(value_head(f64), Some("value.f64"));
881        assert_eq!(load_head(f32), Some("load.f32"));
882        assert_eq!(store_head(f64), Some("store.f64"));
883        assert_eq!(ret_head(f32), Some("ret.f32"));
884        assert_eq!(binary_head(Opcode::FAdd, f32), Some("fadd.f32"));
885        assert_eq!(binary_head(Opcode::FSub, f64), Some("fsub.f64"));
886        assert_eq!(binary_head(Opcode::FMul, f32), Some("fmul.f32"));
887        assert_eq!(binary_head(Opcode::FDiv, f64), Some("fdiv.f64"));
888        // Not one of the four widths an integer rule is written at, and not a constant either,
889        // since what a pattern binds inside an `iconst` is a number and a float is not one.
890        assert_eq!(slot(f32), None);
891        assert_eq!(slot(f64), None);
892        assert_eq!(iconst_head(f64), None);
893        // An integer add at thirty two bits is a different name from a float add at the same
894        // width, which is the whole of what keeps the two rule sets apart.
895        assert_ne!(binary_head(Opcode::Add, Type::int(32)), binary_head(Opcode::FAdd, f32));
896    }
897
898    /// What the machine has no scalar instruction for has no name, so it is reported rather than
899    /// lowered to something near it. A remainder is a call to `fmod` and a `long double` is on the
900    /// x87 stack, and neither is anything a rule in this set is written about.
901    #[test]
902    fn a_float_operation_the_machine_lacks_has_no_name() {
903        assert_eq!(binary_head(Opcode::FRem, Type::float(Float::F32)), None);
904        let long = Type::float(Float::F80);
905        assert_eq!(float_slot(long), None);
906        assert_eq!(value_head(long), None);
907        assert_eq!(binary_head(Opcode::FAdd, long), None);
908        assert_eq!(ret_head(long), None);
909    }
910
911    /// A lane count is not a width, so a rule written at a width does not get to answer for a
912    /// vector of that width. Nothing produces one yet and the day something does it should be
913    /// reported rather than lowered to an instruction that acts on one lane of it.
914    #[test]
915    fn a_vector_is_not_the_width_of_its_lane() {
916        let i32x4 = Type::vector(Type::int(32), 4);
917        assert_eq!(slot(i32x4), None);
918        assert_eq!(value_head(i32x4), None);
919        assert_eq!(binary_head(Opcode::Add, i32x4), None);
920    }
921
922    /// Address arithmetic is named as the add it is, which is what puts it in reach of every rule
923    /// written about one, including the two below that fold it into an address.
924    #[test]
925    fn address_arithmetic_is_an_add_at_the_address_width() {
926        let (mut func, block) = func();
927        let base = func.append_param(block, Type::PTR);
928        let mut build = Builder::new(&mut func, block);
929        let step = build.iconst(Type::int(64), 4);
930        let args = func.push_values(&[base, step]);
931        let next = Builder::new(&mut func, block)
932            .value(rucc_ir::InstData { args, ..rucc_ir::InstData::new(Opcode::PtrAdd) }, Type::PTR);
933        let inst = inst_of(&func, next);
934
935        let terms = Terms::new(&func, inst, [Shown::Reg, Shown::Const, Shown::Reg]);
936        assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
937        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i64", 1)));
938        assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i64", 1)));
939        assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(4));
940    }
941}