Skip to main content

rucc_ir/
term.rs

1//! The IR as something a rule can match against.
2//!
3//! Design: `spec/10-backend.md` section 10.2 and `spec/optimizer/13-rewrite-rules.md`.
4//!
5//! Two rule sets are matched against the IR. `rucc-codegen` lowers it to machine terms and
6//! `rucc-opt` rewrites it to more IR, and both of them are asking what an instruction is called
7//! and what its operands are. This is here rather than in either of them so that there is one
8//! answer to that: a rewrite rule and a lowering rule that spelled `add.i32` differently would
9//! be two vocabularies over one IR, and the day they drifted apart nothing would say so.
10//!
11//! A rule is written about a term and the compiler has no terms. It has a function full of
12//! instructions, and what a pattern is about is one of them together with whatever its operands
13//! were computed from. So this is the [`Subject`] the matcher asks its three questions of, and
14//! the answers come out of the IR: nothing is built and nothing is thrown away.
15//!
16//! # How an operand is shown
17//!
18//! The same IR value can be several different terms. `(add.i32 (value.i32 x) (iconst.i32 k))`
19//! and `(add.i32 (value.i32 x) (value.i32 y))` are two patterns over one instruction, and which
20//! one it is depends on whether the second operand is a constant and on whether the rule that
21//! wants a constant will take this one. `(add.i64 (value.i64 x) (mul.i64 (value.i64 y)
22//! (iconst.i64 4)))` is a third, and it is about two instructions rather than one.
23//!
24//! The matcher does not backtrack across alternatives for one node: [`Subject::head`] gives one
25//! answer and the walk believes it. So the choice is made before the walk rather than during it.
26//! A [`Plan`] says how each operand of the instruction is shown, the caller tries the plans in
27//! order, and the first that matches is the one that fires. There are at most three ways to show
28//! an operand and at most two operands in any pattern either rule set has, so the whole of the
29//! search is a handful of walks over a trie, each of which fails in its first node or two.
30//!
31//! # How deep it goes
32//!
33//! One level. An operand may be shown as the instruction that computed it, and that
34//! instruction's own operands are shown as a register or as a constant and never expanded
35//! again, which is as deep as any pattern in either rule file reaches. A rule set that wants
36//! three levels needs this to grow a level, and it would be found by the rule failing to fire
37//! rather than by anything going wrong.
38
39use rucc_base::rules::Subject;
40
41use crate::{Def, Extra, Float, FloatPred, Func, Inst, IntPred, Opcode, Type, Value};
42
43/// How many operands of one instruction a plan can speak about.
44///
45/// Two is what every pattern in the rule set needs, and a third costs nothing to carry. An
46/// instruction with more operands than this is one no rule matches, which is the same answer it
47/// would get from a plan that could describe it.
48pub const MAX_ARGS: usize = 3;
49
50/// How one operand is shown to the matcher.
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum Shown {
53    /// As a value sitting in a register, which is what `(value.iN x)` matches.
54    Reg,
55    /// As a constant the caller has in hand, which is what `(iconst.iN k)` matches.
56    Const,
57    /// As a register that is not a constant, which is what `(value.iN x)` matches when the
58    /// operand is anything other than a number.
59    ///
60    /// This is [`Shown::Reg`] with the constants refused. A canonicalisation is a rule that
61    /// moves an operand from one side to the other, and the swapped form it writes matches the
62    /// rule again the moment the other side is a constant too, which is a term the pass would
63    /// rewrite until it ran out of fuel. Saying which side is not a number is what stops it, and
64    /// it has to be said in the plan rather than in a guard, because a guard reads a binding as
65    /// a number and is false when it is not one.
66    Var,
67    /// As the instruction that computed it, so a rule can be about two instructions at once.
68    Expand,
69}
70
71/// How every operand of one instruction is shown.
72pub type Plan = [Shown; MAX_ARGS];
73
74/// Everything shown as a register, which is the plan that matches when no other does.
75pub const PLAIN: Plan = [Shown::Reg; MAX_ARGS];
76
77/// One node of the term the matcher is walking.
78///
79/// A position rather than a term, because the term does not exist. Two of these are values in
80/// their own right, and they are the two a pattern can bind: the register a `value` wraps and
81/// the number an `iconst` wraps.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum Term {
84    /// The instruction being matched.
85    Root,
86    /// Operand `i` of the root, shown the way the plan says to show it.
87    Arg(u8),
88    /// Operand `j` of the instruction that computed operand `i` of the root.
89    Deep(u8, u8),
90    /// A value in a register, which is what a pattern binds when it writes `(value.iN x)`.
91    Reg(Value),
92    /// A constant, which is what a pattern binds or tests inside an `(iconst.iN k)`.
93    Num(i128),
94}
95
96/// One instruction of a function, as the terms a rule could match.
97#[derive(Debug)]
98pub struct Terms<'a> {
99    func: &'a Func,
100    root: Inst,
101    plan: Plan,
102}
103
104impl<'a> Terms<'a> {
105    /// The instruction, shown the way the plan says.
106    #[must_use]
107    pub fn new(func: &'a Func, root: Inst, plan: Plan) -> Self {
108        Self { func, root, plan }
109    }
110
111    /// The instruction this is about.
112    #[must_use]
113    pub fn root(&self) -> Inst {
114        self.root
115    }
116
117    /// What the root, or an instruction one of its operands was expanded into, is called in a
118    /// rule file.
119    #[must_use]
120    pub fn name(&self, inst: Inst) -> Option<&'static str> {
121        head_of(self.func, inst)
122    }
123
124    /// The value operands of an instruction.
125    fn args(&self, inst: Inst) -> &[Value] {
126        &self.func[self.func[inst].args]
127    }
128
129    /// Operand `index` of the root, or nothing if it has no such operand.
130    fn arg_value(&self, index: u8) -> Option<Value> {
131        self.args(self.root).get(usize::from(index)).copied()
132    }
133
134    /// The instruction a value is the result of, or nothing for a block parameter.
135    fn def_of(&self, value: Value) -> Option<Inst> {
136        match self.func[value].def {
137            Def::Result { inst, .. } => Some(inst),
138            Def::Param { .. } => None,
139        }
140    }
141
142    /// What a value is, if it is a constant.
143    #[must_use]
144    pub fn constant(&self, value: Value) -> Option<i128> {
145        let inst = self.def_of(value)?;
146        let data = &self.func[inst];
147        if data.opcode != Opcode::IConst {
148            return None;
149        }
150        let Extra::Imm(imm) = data.extra else { return None };
151        let ty = self.func[value].ty;
152        if !ty.is_int() {
153            return None;
154        }
155        // One bit is read unsigned, and every other width is read signed. The sign bit of a one
156        // bit integer is the whole of it, so the signed reading of a true is minus one, and what
157        // a rule at that width means by the number it matched is the truth value rather than a
158        // bit pattern. Reading it signed would put a byte of ones in a register where the rest of
159        // the rule set expects a zero or a one.
160        if is_bit(ty) {
161            return Some(i128::try_from(self.func[imm].unsigned()).unwrap_or(0));
162        }
163        Some(self.func[imm].signed(ty))
164    }
165
166    /// The head of a value shown as a register or as a constant, which is a term of one
167    /// argument either way: the thing the pattern binds.
168    fn leaf_head(&self, value: Value, shown: Shown) -> Option<(&'static str, usize)> {
169        let ty = self.func[value].ty;
170        let name = match shown {
171            Shown::Reg => value_head(ty)?,
172            Shown::Const => iconst_head(ty)?,
173            // A constant shown this way is not shown at all. The head is the only place that can
174            // refuse it, since a binding says nothing about what the operand was called.
175            Shown::Var if self.constant(value).is_none() => value_head(ty)?,
176            Shown::Var => return None,
177            // An expansion is not a leaf, and nothing asks this about one.
178            Shown::Expand => return None,
179        };
180        Some((name, 1))
181    }
182
183    /// What a value shown as a register or as a constant binds, which is the value itself or
184    /// the number it is.
185    fn leaf_arg(&self, value: Value, shown: Shown) -> Term {
186        match shown {
187            Shown::Const => self.constant(value).map_or(Term::Reg(value), Term::Num),
188            Shown::Reg | Shown::Var | Shown::Expand => Term::Reg(value),
189        }
190    }
191
192    /// How an operand of an expanded operand is shown, which is as a constant when it is one
193    /// and as a register otherwise.
194    ///
195    /// There is no choice to make here. The reason to show a constant as a register is that no
196    /// rule would take it as an immediate, and the answer to that inside an expansion is to
197    /// stop expanding, which is a plan the selector tries anyway.
198    fn deep_shown(&self, value: Value) -> Shown {
199        if self.constant(value).is_some() { Shown::Const } else { Shown::Reg }
200    }
201
202    /// The value a place holds, or nothing for a place that holds a constant rather than a
203    /// value.
204    ///
205    /// This is what makes two places comparable. A rule that writes one name twice is asking
206    /// whether both of its operands are the same value, and the two places are operand zero and
207    /// operand one, which are never equal as places.
208    fn value_at(&self, node: Term) -> Option<Value> {
209        match node {
210            Term::Root => self.func[self.root].first_result,
211            Term::Arg(index) => self.arg_value(index),
212            Term::Deep(outer, inner) => {
213                self.expansion(outer).and_then(|(_, args)| args.get(usize::from(inner)).copied())
214            }
215            Term::Reg(value) => Some(value),
216            Term::Num(_) => None,
217        }
218    }
219
220    /// The instruction an expanded operand of the root was computed by, with its operands.
221    fn expansion(&self, index: u8) -> Option<(Inst, &[Value])> {
222        let value = self.arg_value(index)?;
223        let inst = self.def_of(value)?;
224        Some((inst, self.args(inst)))
225    }
226}
227
228impl Subject for Terms<'_> {
229    type Node = Term;
230
231    fn head(&self, node: Term) -> Option<(&str, usize)> {
232        match node {
233            Term::Root => {
234                let name = head_of(self.func, self.root)?;
235                let data = &self.func[self.root];
236                // A constant has no operands and its term has one, which is the constant, so it
237                // is the one instruction whose arity is not the length of its operand list.
238                let arity =
239                    if data.opcode == Opcode::IConst { 1 } else { self.args(self.root).len() };
240                Some((name, arity))
241            }
242            Term::Arg(index) => {
243                let value = self.arg_value(index)?;
244                match self.plan[usize::from(index)] {
245                    Shown::Expand => {
246                        let (inst, args) = self.expansion(index)?;
247                        Some((head_of(self.func, inst)?, args.len()))
248                    }
249                    shown => self.leaf_head(value, shown),
250                }
251            }
252            Term::Deep(outer, inner) => {
253                let (_, args) = self.expansion(outer)?;
254                let value = *args.get(usize::from(inner))?;
255                self.leaf_head(value, self.deep_shown(value))
256            }
257            Term::Reg(_) | Term::Num(_) => None,
258        }
259    }
260
261    fn arg(&self, node: Term, index: usize) -> Term {
262        let index = u8::try_from(index).unwrap_or(u8::MAX);
263        match node {
264            Term::Root => {
265                let data = &self.func[self.root];
266                if data.opcode == Opcode::IConst {
267                    let value = data.first_result.expect("a constant has a result");
268                    return self.leaf_arg(value, Shown::Const);
269                }
270                Term::Arg(index)
271            }
272            Term::Arg(outer) => match self.plan[usize::from(outer)] {
273                Shown::Expand => Term::Deep(outer, index),
274                shown => {
275                    self.arg_value(outer).map_or(Term::Num(0), |value| self.leaf_arg(value, shown))
276                }
277            },
278            Term::Deep(outer, inner) => {
279                let value = self
280                    .expansion(outer)
281                    .and_then(|(_, args)| args.get(usize::from(inner)).copied());
282                value.map_or(Term::Num(0), |value| self.leaf_arg(value, self.deep_shown(value)))
283            }
284            // Neither has a head, so nothing asks either of them for an argument.
285            Term::Reg(_) | Term::Num(_) => node,
286        }
287    }
288
289    fn int(&self, node: Term) -> Option<i128> {
290        match node {
291            Term::Num(value) => Some(value),
292            _ => None,
293        }
294    }
295
296    fn same(&self, a: Term, b: Term) -> bool {
297        match (self.value_at(a), self.value_at(b)) {
298            (Some(left), Some(right)) => left == right,
299            // Neither is a value, so the only other thing either can be is a constant the plan
300            // asked to be shown as one. Two constants of the same number are the same term
301            // whatever computed them, which is the one case where this is not an identity.
302            _ => match (self.int(a), self.int(b)) {
303                (Some(left), Some(right)) => left == right,
304                _ => false,
305            },
306        }
307    }
308}
309
310/// What an instruction is called in a rule file, or nothing if the rules have no name for it.
311///
312/// The one function here that a caller with an [`Inst`] and no [`Terms`] wants, which is
313/// anything reporting on a rule rather than matching one.
314///
315/// The name carries the width, because a rule file that did not say how wide a term is would be
316/// a file whose reader has to look at the line above to find out. Which widths there are names
317/// for is the rule language's business and not this crate's: an instruction at a width nothing
318/// is written about has no name here, and the answer to it is that no rule matches.
319pub fn head_of(func: &Func, inst: Inst) -> Option<&'static str> {
320    let data = &func[inst];
321
322    // A store is the one instruction with a name here that computes nothing, so the width in
323    // its name is the width of what it is storing and has to come from an operand. That operand
324    // is the first one, which is the order `crate::Builder::store` puts them in and the order
325    // a pattern for one is written in.
326    //
327    // Nothing looks at the flags or the ordering, and both of those are worth saying out loud.
328    // A `volatile` access has to happen exactly once and must not move, and neither of those is
329    // something selection does: one IR load is one instruction whatever its flags say, and
330    // folding the address arithmetic into the addressing mode does not change how many times
331    // memory is touched. An ordering would be a different matter, because a store that releases
332    // is not a plain `mov` on any machine where it means anything, but an ordered access is
333    // `atomic_load` or `atomic_store` and those are different opcodes with no name here. The IR
334    // verifier is what makes that true rather than merely usual: it rejects an ordering on a
335    // plain access, so by the time anything is selected there is none to miss.
336    if data.opcode == Opcode::Store {
337        let value = *func[data.args].first()?;
338        return store_head(func[value].ty);
339    }
340
341    // A return is the other one, and the width comes from the operand for the same reason. A
342    // return of nothing has no name, and neither has a return of more than one value: a rule
343    // for either would have to say where each of them goes, and where a value goes is a fact
344    // about the convention rather than about a term, so the rule language has nothing to say
345    // about it. A return of nothing needs no rule at all, since the epilogue is the whole of it.
346    if data.opcode == Opcode::Return {
347        let [value] = &func[data.args] else { return None };
348        return ret_head(func[*value].ty);
349    }
350
351    // A conditional branch is the third instruction here that computes nothing. Where it goes is
352    // not part of its name and not part of any pattern: a machine IR block holds its own
353    // successors, so a rule for a branch never has to say a block, and what is left for it to say
354    // is what the branch is about, which is the condition.
355    if data.opcode == Opcode::BrIf {
356        let [cond] = &func[data.args] else { return None };
357        return (func[*cond].ty == Type::int(1)).then_some(BRIF);
358    }
359
360    let result = data.first_result?;
361    let ty = func[result].ty;
362    match data.opcode {
363        Opcode::IConst => iconst_head(ty),
364        Opcode::Load => load_head(ty),
365        Opcode::ICmp => {
366            let Extra::IntPred(pred) = data.extra else { return None };
367            Some(icmp_head(pred))
368        }
369        // A float comparison, whose name comes from the operands rather than from the result: the
370        // result is one bit either way and what tells the two instructions apart is the format.
371        Opcode::FCmp => {
372            let Extra::FloatPred(pred) = data.extra else { return None };
373            fcmp_head(pred, func[*func[data.args].first()?].ty)
374        }
375        Opcode::SExt | Opcode::ZExt | Opcode::Trunc => {
376            let from = func[*func[data.args].first()?].ty;
377            convert_head(data.opcode, from, ty)
378        }
379        // The conversions with a float on one side or both. A separate row because what is on
380        // each side is part of the name and a width alone would not say which register file the
381        // value is in, which is the whole difference between these and the three above.
382        Opcode::FPExt | Opcode::FPTrunc | Opcode::FPToSI | Opcode::SIToFP | Opcode::Bitcast => {
383            let from = func[*func[data.args].first()?].ty;
384            cross_head(data.opcode, from, ty)
385        }
386        // Address arithmetic is an add at the address width, which is all it is once both
387        // operands are in registers: the offset is already in bytes, which the IR guarantees and
388        // the front end is what did the multiplying. Calling it that is what lets every rule
389        // written about an add reach it, including the ones that fold it into an addressing mode,
390        // and there is nothing in any of them it could get wrong.
391        Opcode::PtrAdd => binary_head(Opcode::Add, ty),
392        opcode => binary_head(opcode, ty),
393    }
394}
395
396/// What a conditional branch is called, which carries the width of the condition and nothing
397/// else, since where the branch goes is on the block rather than in the term.
398///
399/// A constant rather than a literal in [`head_of`] because [`heads`] says it too, and a name
400/// written in two places is a name that can differ in one of them.
401const BRIF: &str = "brif.i1";
402
403/// Every name this module can give an instruction, with the opcode it gives it to.
404///
405/// This is what a rule file could be written about, so that the back end's coverage check can ask what one
406/// is written about and say where the difference is. It comes out of the same functions
407/// [`head_of`] asks rather than out of a list, because a list of names checked against another
408/// list of names is a test that both were typed the same way, which is not the question worth
409/// asking.
410///
411/// The sweep is over every type the compiler has, including the ones nothing here has a name for.
412/// A width with no name contributes nothing and costs nothing, and the day one of them gets a name
413/// it appears here without anybody remembering to add it, which is the property that makes this
414/// worth generating rather than writing down.
415pub fn heads() -> Vec<(Opcode, &'static str)> {
416    let types = [
417        Type::int(1),
418        Type::int(8),
419        Type::int(16),
420        Type::int(32),
421        Type::int(64),
422        Type::int(128),
423        Type::PTR,
424        Type::float(Float::F16),
425        Type::float(Float::F32),
426        Type::float(Float::F64),
427        Type::float(Float::F80),
428        Type::float(Float::F128),
429        Type::vector(Type::int(32), 4),
430    ];
431
432    let mut found = Vec::new();
433    for opcode in Opcode::all() {
434        // The names that come from one type, which is the result's for most of these and an
435        // operand's for the two that compute nothing. The arms are the ones `head_of` has, in the
436        // order it has them, so that a name reachable there is reachable here.
437        for &ty in &types {
438            let name = match opcode {
439                Opcode::Store => store_head(ty),
440                Opcode::Return => ret_head(ty),
441                Opcode::IConst => iconst_head(ty),
442                Opcode::Load => load_head(ty),
443                Opcode::PtrAdd => binary_head(Opcode::Add, ty),
444                _ => binary_head(opcode, ty),
445            };
446            if let Some(name) = name {
447                found.push((opcode, name));
448            }
449        }
450        // And the names that come from a predicate or from two types at once.
451        match opcode {
452            Opcode::BrIf => found.push((opcode, BRIF)),
453            Opcode::ICmp => found.extend(IntPred::all().map(|pred| (opcode, icmp_head(pred)))),
454            Opcode::FCmp => {
455                for pred in FloatPred::all() {
456                    let named = types.iter().filter_map(|&ty| fcmp_head(pred, ty));
457                    found.extend(named.map(|name| (opcode, name)));
458                }
459            }
460            Opcode::SExt | Opcode::ZExt | Opcode::Trunc => {
461                for &from in &types {
462                    let named = types.iter().filter_map(|&to| convert_head(opcode, from, to));
463                    found.extend(named.map(|name| (opcode, name)));
464                }
465            }
466            Opcode::FPExt | Opcode::FPTrunc | Opcode::FPToSI | Opcode::SIToFP | Opcode::Bitcast => {
467                for &from in &types {
468                    let named = types.iter().filter_map(|&to| cross_head(opcode, from, to));
469                    found.extend(named.map(|name| (opcode, name)));
470                }
471            }
472            _ => {}
473        }
474    }
475
476    found.sort_unstable();
477    found.dedup();
478    found
479}
480
481/// How wide an address is on the machine this lowers for.
482///
483/// The rule set has no term for a pointer and needs none. An address in a register is an integer
484/// of the machine's address width, every rule that could compute one is a rule about an integer
485/// of that width, and the only thing missing was a name. [`slot`] used to ask the type how wide
486/// it was, and a pointer answers nothing, because how wide an address is belongs to the target
487/// rather than to the IR. So this is where the target's answer is written down.
488///
489/// Sixty four, and a constant rather than something asked of a target, because every
490/// architecture `rucc_target::Arch` names is a sixty four bit one. There is no target in the
491/// compiler that would want a different number, and a thirty two bit one would want more from
492/// the rule sets than a number.
493pub const ADDRESS: u32 = 64;
494
495/// Which of the four widths a type is, or nothing for a width no rule is written at.
496///
497/// A pointer is one of them, at [`ADDRESS`]. A vector is none of them however wide its lane is,
498/// because a rule at a width says nothing about how many lanes it acts on and lowering an add of
499/// four lanes to an add of one would be wrong rather than incomplete.
500pub fn slot(ty: Type) -> Option<usize> {
501    if !ty.is_scalar() {
502        return None;
503    }
504    let bits = if ty.is_ptr() { ADDRESS } else { ty.is_int().then(|| ty.bits())? };
505    match bits {
506        8 => Some(0),
507        16 => Some(1),
508        32 => Some(2),
509        64 => Some(3),
510        _ => None,
511    }
512}
513
514/// Which of the two float widths a type is, or nothing for anything that is not a float.
515///
516/// Two rather than [`slot`]'s four, and a table of its own rather than more entries in that one,
517/// because a `float` and an `int` of the same width are not the same term to any rule: they are in
518/// different register files and every instruction that touches them is a different instruction. A
519/// `long double` is none of them, since it is on the x87 stack rather than in a vector register
520/// and nothing here is written about that stack.
521pub fn float_slot(ty: Type) -> Option<usize> {
522    if !ty.is_scalar() || !ty.is_float() {
523        return None;
524    }
525    match ty.bits() {
526        32 => Some(0),
527        64 => Some(1),
528        _ => None,
529    }
530}
531
532/// Whether a type is the float the machine moves but does not compute in at the narrow end.
533///
534/// Sixteen bits, which is `_Float16`. It is not one of [`float_slot`]'s two for the same reason
535/// [`is_quad`] is not: the answer is a width no arithmetic on this machine is written at, so an
536/// entry in that table would hand every rule reading it an index the list of two names does not
537/// have.
538///
539/// What the machine does have for it is the moves, and only barely. SSE2 puts sixteen bits into a
540/// vector register with `pinsrw` and takes them out with `pextrw`, both through the low lane, which
541/// is where the psABI says a value of this format lives. Everything else is a call: there is no
542/// half precision addition, comparison or conversion below `-mavx512fp16`, which is above this
543/// target's baseline, so `crate::half`'s pass turns each of those into the work at a wider format
544/// with a runtime call on each side of it. gcc 16 does exactly the same thing at the same baseline.
545///
546/// The one asymmetry worth knowing about is the store. A load of one is a single `pinsrw` from
547/// memory, and a store of one is not a single instruction, because the form of `pextrw` that writes
548/// memory is SSE4.1. So the pass rewrites the store into a sixteen bit integer store of the bits
549/// and the rule set answers the load, which is the split gcc's output has as well.
550#[must_use]
551pub fn is_half(ty: Type) -> bool {
552    ty.is_scalar() && ty.is_float() && ty.bits() == 16
553}
554
555/// Whether a type is the float the machine moves and does not compute in.
556///
557/// A hundred and twenty eight bits of float, which is `_Float128` and is `long double` on the
558/// targets whose `long double` is that format. It is not one of [`float_slot`]'s two, and it is a
559/// question of its own for the same reason [`is_bit`] is: the answer is a width no arithmetic is
560/// written at, so putting it in that table would hand every rule there an index its list of two
561/// names does not have.
562///
563/// What the machine does have for it is the moves. A vector register holds sixteen bytes, `movaps`
564/// moves all sixteen of them, and neither instruction looks at what it moves, so a value of this
565/// width can be loaded, stored, passed, returned and copied. What it cannot be is added, compared
566/// or converted: there is no instruction for any of those at this format, so every one of them is a
567/// call to the runtime, and until the pass that writes those calls exists an arithmetic reaching
568/// the selector finds no name here and is reported.
569///
570/// An eighty bit `long double` is not this. That one is on the x87 stack rather than in a vector
571/// register, which is a file nothing here allocates, and `crate::term` has no name for it at all.
572#[must_use]
573pub fn is_quad(ty: Type) -> bool {
574    ty.is_scalar() && ty.is_float() && ty.bits() == 128
575}
576
577/// Whether a value of that type lives in a vector register, which is the two formats the machine
578/// computes in plus the one it only moves.
579///
580/// The question a register file is picked by, asked here so that the three places that pick one are
581/// reading the same answer. A value put in the wrong file is a value every instruction that then
582/// touches it is the wrong instruction for.
583#[must_use]
584pub fn in_vector_file(ty: Type) -> bool {
585    float_slot(ty).is_some() || is_quad(ty) || is_half(ty)
586}
587
588/// Whether a type is the one bit a truth value comes in.
589///
590/// One bit is a width the rule set is written at and is not one of [`slot`]'s four, because it is
591/// not a width the machine computes in. There is no one bit register and no one bit instruction: a
592/// value of this width lives in a whole byte with the other seven bits zero, which is what a
593/// `setcc` leaves behind, and every rule written at one bit is a byte instruction chosen because
594/// it keeps that true. The model says the same thing from the other side, giving `setcc` a meaning
595/// one bit wide, so the abstraction is stated in both places rather than assumed in either.
596///
597/// What makes the invariant hold rather than merely be usual is the short list of places a value
598/// of this width can come from. A comparison produces a zero or a one, a constant at this width is
599/// written as one, the three bitwise operations carry those through unchanged, and everything else
600/// at one bit reaches [`slot`] and gets nothing. A load is the one that reaches outside the
601/// compiler, since it gives back whatever byte was at the address, and what says that byte is a
602/// zero or a one is C rather than the machine: the value of a `_Bool` object holding anything else
603/// is undefined. The store at this width is what keeps that true from the other side, because the
604/// only values it can be handed are the ones this paragraph lists.
605pub fn is_bit(ty: Type) -> bool {
606    ty.is_scalar() && ty.is_int() && ty.bits() == 1
607}
608
609/// What a value in a register is called at that width.
610fn value_head(ty: Type) -> Option<&'static str> {
611    if is_bit(ty) {
612        return Some("value.i1");
613    }
614    if is_quad(ty) {
615        return Some("value.f128");
616    }
617    if is_half(ty) {
618        return Some("value.f16");
619    }
620    if let Some(at) = float_slot(ty) {
621        return Some(["value.f32", "value.f64"][at]);
622    }
623    Some(["value.i8", "value.i16", "value.i32", "value.i64"][slot(ty)?])
624}
625
626/// What a constant is called at that width.
627///
628/// An integer and not an address, unlike everything else here. What a pattern binds inside one of
629/// these is the number, and [`Terms::constant`] only has a number for an integer, so a term that
630/// named an address would be one a rule could match and then find nothing behind.
631fn iconst_head(ty: Type) -> Option<&'static str> {
632    if !ty.is_int() {
633        return None;
634    }
635    if is_bit(ty) {
636        return Some("iconst.i1");
637    }
638    Some(["iconst.i8", "iconst.i16", "iconst.i32", "iconst.i64"][slot(ty)?])
639}
640
641/// What a load is called, which is the width of the value it produced.
642fn load_head(ty: Type) -> Option<&'static str> {
643    if is_quad(ty) {
644        return Some("load.f128");
645    }
646    if is_half(ty) {
647        return Some("load.f16");
648    }
649    if let Some(at) = float_slot(ty) {
650        return Some(["load.f32", "load.f64"][at]);
651    }
652    if is_bit(ty) {
653        return Some("load.i1");
654    }
655    Some(["load.i8", "load.i16", "load.i32", "load.i64"][slot(ty)?])
656}
657
658/// What a store is called, which is the width of the value it writes, since it produces nothing
659/// to take a width from.
660fn store_head(ty: Type) -> Option<&'static str> {
661    if is_quad(ty) {
662        return Some("store.f128");
663    }
664    if let Some(at) = float_slot(ty) {
665        return Some(["store.f32", "store.f64"][at]);
666    }
667    if is_bit(ty) {
668        return Some("store.i1");
669    }
670    Some(["store.i8", "store.i16", "store.i32", "store.i64"][slot(ty)?])
671}
672
673/// What a return is called, which is the width of the value it gives back, for the same reason.
674fn ret_head(ty: Type) -> Option<&'static str> {
675    if is_quad(ty) {
676        return Some("ret.f128");
677    }
678    if is_half(ty) {
679        return Some("ret.f16");
680    }
681    if let Some(at) = float_slot(ty) {
682        return Some(["ret.f32", "ret.f64"][at]);
683    }
684    if is_bit(ty) {
685        return Some("ret.i1");
686    }
687    Some(["ret.i8", "ret.i16", "ret.i32", "ret.i64"][slot(ty)?])
688}
689
690/// What a comparison is called, which does not carry the width of what it compared: the result
691/// is one bit whatever the operands were, and the operands say how wide they are themselves.
692fn icmp_head(pred: IntPred) -> &'static str {
693    match pred {
694        IntPred::Eq => "icmp_eq.i1",
695        IntPred::Ne => "icmp_ne.i1",
696        IntPred::Slt => "icmp_slt.i1",
697        IntPred::Sle => "icmp_sle.i1",
698        IntPred::Sgt => "icmp_sgt.i1",
699        IntPred::Sge => "icmp_sge.i1",
700        IntPred::Ult => "icmp_ult.i1",
701        IntPred::Ule => "icmp_ule.i1",
702        IntPred::Ugt => "icmp_ugt.i1",
703        IntPred::Uge => "icmp_uge.i1",
704    }
705}
706
707/// The predicate a head names, when the head is a comparison of two integers.
708///
709/// The inverse of `icmp_head`, which is private, and a search over it rather than a second table, because two
710/// tables that are supposed to be inverses are two tables that will stop being inverses. Ten
711/// comparisons is a short enough search that the alternative would be arranging for a map to be
712/// built once, and this is asked once per rule that fires rather than once per instruction.
713///
714/// What wants this is the peephole. A rule may write a comparison, and the predicate is not part
715/// of the opcode: [`heads`] gives every predicate the same [`Opcode::ICmp`], so a rewriter that
716/// asked only for the opcode would build a comparison with whatever predicate happened to be on
717/// the instruction it replaced. That is not an instruction computing something else, it is one
718/// computing the opposite.
719pub fn int_pred(head: &str) -> Option<IntPred> {
720    IntPred::all().find(|&pred| icmp_head(pred) == head)
721}
722
723/// What a float comparison is called, which does carry the format of what it compared.
724///
725/// The difference from [`icmp_head`] is the whole reason this is a second function. A comparison
726/// of two integers is the same instruction whatever file they came from, because there is only one
727/// file they could have come from, so the width lives on the operands and the name says nothing
728/// about it. A comparison of two floats is a different instruction for a `float` and a `double`,
729/// and the operands are in registers that hold either, so the name has to say which.
730///
731/// The two predicates that read nothing have no name here. `false` and `true` do not look at their
732/// operands, so a rule for either would be a rule that computes a constant out of a comparison it
733/// did not make, and the front end writes neither: nothing in C spells them and nothing here folds
734/// a comparison into one yet.
735fn fcmp_head(pred: FloatPred, ty: Type) -> Option<&'static str> {
736    let at = float_slot(ty)?;
737    let names: [&'static str; 2] = match pred {
738        FloatPred::Oeq => ["fcmp_oeq.f32.i1", "fcmp_oeq.f64.i1"],
739        FloatPred::Ogt => ["fcmp_ogt.f32.i1", "fcmp_ogt.f64.i1"],
740        FloatPred::Oge => ["fcmp_oge.f32.i1", "fcmp_oge.f64.i1"],
741        FloatPred::Olt => ["fcmp_olt.f32.i1", "fcmp_olt.f64.i1"],
742        FloatPred::Ole => ["fcmp_ole.f32.i1", "fcmp_ole.f64.i1"],
743        FloatPred::One => ["fcmp_one.f32.i1", "fcmp_one.f64.i1"],
744        FloatPred::Ord => ["fcmp_ord.f32.i1", "fcmp_ord.f64.i1"],
745        FloatPred::Uno => ["fcmp_uno.f32.i1", "fcmp_uno.f64.i1"],
746        FloatPred::Ueq => ["fcmp_ueq.f32.i1", "fcmp_ueq.f64.i1"],
747        FloatPred::Ugt => ["fcmp_ugt.f32.i1", "fcmp_ugt.f64.i1"],
748        FloatPred::Uge => ["fcmp_uge.f32.i1", "fcmp_uge.f64.i1"],
749        FloatPred::Ult => ["fcmp_ult.f32.i1", "fcmp_ult.f64.i1"],
750        FloatPred::Ule => ["fcmp_ule.f32.i1", "fcmp_ule.f64.i1"],
751        FloatPred::Une => ["fcmp_une.f32.i1", "fcmp_une.f64.i1"],
752        FloatPred::False | FloatPred::True => return None,
753    };
754    Some(names[at])
755}
756
757/// What a conversion is called, which is the two widths it is between.
758///
759/// The two conversions one bit has are a row and a column of their own rather than a fifth entry
760/// in the tables below. A five by five table would have a name for every conversion between one
761/// bit and every other width in both directions, and half of those are conversions nothing writes:
762/// a sign extension from one bit is what an `unsigned` comparison result would need and there is
763/// none, and neither of the other two opcodes narrows.
764///
765/// What writes the narrowing is worth saying, because a conversion to `_Bool` is not one. C says
766/// that conversion is a comparison against zero, and it reaches the IR as an `icmp`. A `trunc` to
767/// one bit is what a bit field of width one whose type is a `_Bool` needs, where the front end has
768/// already brought the bit down to the bottom of a wider value and what is left is to say that the
769/// bottom of it is the whole of the value.
770fn convert_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
771    if is_bit(from) {
772        if opcode != Opcode::ZExt {
773            return None;
774        }
775        return Some(["zext.i1.i8", "zext.i1.i16", "zext.i1.i32", "zext.i1.i64"][slot(to)?]);
776    }
777    if is_bit(to) {
778        if opcode != Opcode::Trunc {
779            return None;
780        }
781        return Some(["trunc.i8.i1", "trunc.i16.i1", "trunc.i32.i1", "trunc.i64.i1"][slot(from)?]);
782    }
783    let table: &[[Option<&'static str>; 4]; 4] = match opcode {
784        Opcode::SExt => &SEXT,
785        Opcode::ZExt => &ZEXT,
786        Opcode::Trunc => &TRUNC,
787        _ => return None,
788    };
789    table[slot(from)?][slot(to)?]
790}
791
792/// Which of the two integer widths a conversion to or from a float is written at, or nothing for
793/// any other width.
794///
795/// The machine converts at thirty two bits and at sixty four and at no width below them. A C
796/// program turning a `double` into a `short` is a conversion to `int` and a truncation after it,
797/// and the front end is what writes the truncation, so a narrower conversion arriving here has no
798/// name and is reported rather than lowered to an instruction that would round it in the wrong
799/// place.
800fn cross_slot(ty: Type) -> Option<usize> {
801    match slot(ty)? {
802        2 => Some(0),
803        3 => Some(1),
804        _ => None,
805    }
806}
807
808/// Whether that type is the integer the float at that index shares its width with.
809///
810/// A pointer is not, however wide it is. The IR has `ptrtoint` for turning an address into a
811/// number, and a `bitcast` that moved one through a vector register would be hiding that
812/// conversion rather than performing it, which is what the IR verifier says as well.
813fn paired_int(ty: Type, at: usize) -> bool {
814    ty.is_scalar() && ty.is_int() && ty.bits() == [32, 64][at]
815}
816
817/// What a conversion with a float on one side or both is called, which is what it goes between and
818/// which side each of them is on.
819///
820/// The name carries the format where an integer conversion carries a width, for the reason
821/// [`float_slot`] gives: a `float` and an `int` of the same width are in different register files
822/// and no rule written about one says anything about the other. So there is no name here that
823/// could be read as either, and a rule for `fptosi.f64.i32` cannot match anything but a `double`
824/// becoming an `int`.
825///
826/// The unsigned conversions have no name. The machine has no instruction for either below a
827/// register wider than anything this allocates, so each is several instructions and belongs in a
828/// pass that rewrites it into these rather than in a rule that would have to be several
829/// instructions long.
830fn cross_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
831    match opcode {
832        // Between the two formats, one name each way. There is no third format with a name here,
833        // so these two are the whole of it rather than the first two of a table.
834        Opcode::FPExt => {
835            (float_slot(from)? == 0 && float_slot(to)? == 1).then_some("fpext.f32.f64")
836        }
837        Opcode::FPTrunc => {
838            (float_slot(from)? == 1 && float_slot(to)? == 0).then_some("fptrunc.f64.f32")
839        }
840        Opcode::FPToSI => Some(FPTOSI[float_slot(from)?][cross_slot(to)?]),
841        Opcode::SIToFP => Some(SITOFP[cross_slot(from)?][float_slot(to)?]),
842        // A reinterpretation, which is a `movd` or a `movq` between the two register files and is
843        // the one conversion here that changes no bit. Between two integers or between two floats
844        // it is nothing at all, since the IR keeps the width the same, so the four that cross the
845        // files are the four with a name.
846        Opcode::Bitcast => {
847            // The half is a third pair and not a third entry in the two lists below, because it is
848            // not one of [`float_slot`]'s widths. It is here rather than left out because these
849            // two are the whole of what this machine does with the format: `crate::half`'s pass
850            // rewrites a store of one and a constant of one into the bits and one of these, and a
851            // rule turns each of them into the lane move the machine has.
852            if is_half(from) && to.is_scalar() && to.is_int() && to.bits() == 16 {
853                return Some("bitcast.f16.i16");
854            }
855            if is_half(to) && from.is_scalar() && from.is_int() && from.bits() == 16 {
856                return Some("bitcast.i16.f16");
857            }
858            match (float_slot(from), float_slot(to)) {
859                (Some(at), None) if paired_int(to, at) => {
860                    Some(["bitcast.f32.i32", "bitcast.f64.i64"][at])
861                }
862                (None, Some(at)) if paired_int(from, at) => {
863                    Some(["bitcast.i32.f32", "bitcast.i64.f64"][at])
864                }
865                _ => None,
866            }
867        }
868        _ => None,
869    }
870}
871
872/// A float to a signed integer, from the format down the side to the width across the top.
873static FPTOSI: [[&str; 2]; 2] =
874    [["fptosi.f32.i32", "fptosi.f32.i64"], ["fptosi.f64.i32", "fptosi.f64.i64"]];
875
876/// A signed integer to a float, the other way round.
877static SITOFP: [[&str; 2]; 2] =
878    [["sitofp.i32.f32", "sitofp.i32.f64"], ["sitofp.i64.f32", "sitofp.i64.f64"]];
879
880/// What each of the binary operations is called at each width.
881///
882/// The three bitwise ones are the only ones with a name at one bit. They are what a `!=` between
883/// two truth values and a `&&` folded to one instruction become, and each of them takes two bytes
884/// 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
885/// add or a shift at this width and no front end writes one.
886fn binary_head(opcode: Opcode, ty: Type) -> Option<&'static str> {
887    if is_bit(ty) {
888        return match opcode {
889            Opcode::And => Some("and.i1"),
890            Opcode::Or => Some("or.i1"),
891            Opcode::Xor => Some("xor.i1"),
892            _ => None,
893        };
894    }
895    if let Some(at) = float_slot(ty) {
896        // The four the machine has one instruction each for. A remainder is not among them: there
897        // is no scalar instruction for it and what C means by `fmod` is a call, so an `frem` that
898        // reached here would find no rule and be reported rather than lowered to something else.
899        let names: &[&'static str; 2] = match opcode {
900            Opcode::FAdd => &["fadd.f32", "fadd.f64"],
901            Opcode::FSub => &["fsub.f32", "fsub.f64"],
902            Opcode::FMul => &["fmul.f32", "fmul.f64"],
903            Opcode::FDiv => &["fdiv.f32", "fdiv.f64"],
904            _ => return None,
905        };
906        return Some(names[at]);
907    }
908    let names: &[&'static str; 4] = match opcode {
909        Opcode::Add => &["add.i8", "add.i16", "add.i32", "add.i64"],
910        Opcode::Sub => &["sub.i8", "sub.i16", "sub.i32", "sub.i64"],
911        Opcode::Mul => &["mul.i8", "mul.i16", "mul.i32", "mul.i64"],
912        Opcode::SDiv => &["sdiv.i8", "sdiv.i16", "sdiv.i32", "sdiv.i64"],
913        Opcode::UDiv => &["udiv.i8", "udiv.i16", "udiv.i32", "udiv.i64"],
914        Opcode::SRem => &["srem.i8", "srem.i16", "srem.i32", "srem.i64"],
915        Opcode::URem => &["urem.i8", "urem.i16", "urem.i32", "urem.i64"],
916        Opcode::And => &["and.i8", "and.i16", "and.i32", "and.i64"],
917        Opcode::Or => &["or.i8", "or.i16", "or.i32", "or.i64"],
918        Opcode::Xor => &["xor.i8", "xor.i16", "xor.i32", "xor.i64"],
919        Opcode::Shl => &["shl.i8", "shl.i16", "shl.i32", "shl.i64"],
920        Opcode::LShr => &["lshr.i8", "lshr.i16", "lshr.i32", "lshr.i64"],
921        Opcode::AShr => &["ashr.i8", "ashr.i16", "ashr.i32", "ashr.i64"],
922        // Named by the width of the two arms, which is the width of the answer. The bit that
923        // chooses is one bit whatever they are, so it says nothing about which instruction this
924        // is and is not in the name.
925        Opcode::Select => &["select.i8", "select.i16", "select.i32", "select.i64"],
926        _ => return None,
927    };
928    Some(names[slot(ty)?])
929}
930
931/// The widening conversions, from the width down the side to the width across the top. The
932/// diagonal and everything below it is empty, because a sign extension to a width it already
933/// has is not an instruction and the IR does not have one.
934static SEXT: [[Option<&str>; 4]; 4] = [
935    [None, Some("sext.i8.i16"), Some("sext.i8.i32"), Some("sext.i8.i64")],
936    [None, None, Some("sext.i16.i32"), Some("sext.i16.i64")],
937    [None, None, None, Some("sext.i32.i64")],
938    [None, None, None, None],
939];
940
941static ZEXT: [[Option<&str>; 4]; 4] = [
942    [None, Some("zext.i8.i16"), Some("zext.i8.i32"), Some("zext.i8.i64")],
943    [None, None, Some("zext.i16.i32"), Some("zext.i16.i64")],
944    [None, None, None, Some("zext.i32.i64")],
945    [None, None, None, None],
946];
947
948/// The narrowing ones, which fill the other corner for the same reason.
949static TRUNC: [[Option<&str>; 4]; 4] = [
950    [None, None, None, None],
951    [Some("trunc.i16.i8"), None, None, None],
952    [Some("trunc.i32.i8"), Some("trunc.i32.i16"), None, None],
953    [Some("trunc.i64.i8"), Some("trunc.i64.i16"), Some("trunc.i64.i32"), None],
954];
955
956#[cfg(test)]
957mod tests {
958    use rucc_base::Interner;
959
960    use super::*;
961    use crate::{Builder, Flags, Signature};
962
963    /// A function with one block, and the builder to put instructions in it.
964    fn func() -> (Func, crate::Block) {
965        let mut names = Interner::new();
966        let mut func = Func::new(names.intern("f"), Signature::new());
967        let block = func.create_block();
968        (func, block)
969    }
970
971    /// The instruction that computed a value, which every value in these tests has.
972    fn inst_of(func: &Func, value: Value) -> Inst {
973        match func[value].def {
974            Def::Result { inst, .. } => inst,
975            Def::Param { .. } => unreachable!(),
976        }
977    }
978
979    #[test]
980    fn an_instruction_is_the_term_the_rule_file_names_it_by() {
981        let (mut func, block) = func();
982        let i32 = Type::int(32);
983        let mut build = Builder::new(&mut func, block);
984        let k = build.iconst(i32, 7);
985        let x = build.iconst(i32, 3);
986        let sum = build.binary(Opcode::Add, x, k, Flags::default());
987        let add = inst_of(&func, sum);
988
989        let terms = Terms::new(&func, add, PLAIN);
990        assert_eq!(terms.head(Term::Root), Some(("add.i32", 2)));
991        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i32", 1)));
992        assert_eq!(terms.arg(Term::Arg(0), 0), Term::Reg(x));
993        assert_eq!(terms.head(Term::Reg(x)), None);
994        assert_eq!(terms.int(Term::Reg(x)), None);
995    }
996
997    /// What a pattern writing one name in two places asks. The two operands of `x & x` are
998    /// operand zero and operand one, so the question is about the values in them and not about
999    /// the places, and `spec/optimizer/13-rewrite-rules.md` section 13.4 has four identities
1000    /// that cannot be written without it.
1001    #[test]
1002    fn two_places_are_the_same_term_when_the_same_value_is_in_both() {
1003        let (mut func, block) = func();
1004        let i32 = Type::int(32);
1005        let mut build = Builder::new(&mut func, block);
1006        let x = build.iconst(i32, 3);
1007        let y = build.iconst(i32, 5);
1008        let both = build.binary(Opcode::And, x, x, Flags::default());
1009        let apart = build.binary(Opcode::And, x, y, Flags::default());
1010
1011        let terms = Terms::new(&func, inst_of(&func, both), PLAIN);
1012        let left = terms.arg(Term::Arg(0), 0);
1013        let right = terms.arg(Term::Arg(1), 0);
1014        assert_ne!(Term::Arg(0), Term::Arg(1));
1015        assert!(terms.same(left, right));
1016
1017        let terms = Terms::new(&func, inst_of(&func, apart), PLAIN);
1018        let left = terms.arg(Term::Arg(0), 0);
1019        let right = terms.arg(Term::Arg(1), 0);
1020        assert!(!terms.same(left, right));
1021    }
1022
1023    /// Two operands shown as constants are the same term when they are the same number, whatever
1024    /// computed each of them. That is the one case where this is not identity of a value, and it
1025    /// is right: a rule about `x - x` is about what the operands are, and two `3`s are one term.
1026    #[test]
1027    fn two_constants_of_one_number_are_the_same_term() {
1028        let (mut func, block) = func();
1029        let i32 = Type::int(32);
1030        let mut build = Builder::new(&mut func, block);
1031        let x = build.iconst(i32, 3);
1032        let y = build.iconst(i32, 3);
1033        let sum = build.binary(Opcode::Add, x, y, Flags::default());
1034
1035        let terms = Terms::new(&func, inst_of(&func, sum), [Shown::Const; MAX_ARGS]);
1036        let left = terms.arg(Term::Arg(0), 0);
1037        let right = terms.arg(Term::Arg(1), 0);
1038        assert_ne!(x, y);
1039        assert_eq!((left, right), (Term::Num(3), Term::Num(3)));
1040        assert!(terms.same(left, right));
1041        // And a constant is not the value beside it, because one of them has a number and the
1042        // other has not.
1043        assert!(!terms.same(left, Term::Reg(y)));
1044    }
1045
1046    #[test]
1047    fn an_operand_shown_as_a_constant_gives_the_number_up() {
1048        let (mut func, block) = func();
1049        let i32 = Type::int(32);
1050        let mut build = Builder::new(&mut func, block);
1051        let x = build.iconst(i32, 3);
1052        let k = build.iconst(i32, -7);
1053        let sum = build.binary(Opcode::Add, x, k, Flags::default());
1054        let add = inst_of(&func, sum);
1055
1056        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Const, Shown::Reg]);
1057        assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i32", 1)));
1058        assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(-7));
1059        assert_eq!(terms.int(Term::Num(-7)), Some(-7));
1060        // The same operand shown as a register is a register, and a guard asking what number it
1061        // is gets no answer, which is what makes a rule about a number decline it.
1062        let plain = Terms::new(&func, add, PLAIN);
1063        assert_eq!(plain.head(Term::Arg(1)), Some(("value.i32", 1)));
1064        assert_eq!(plain.int(plain.arg(Term::Arg(1), 0)), None);
1065    }
1066
1067    /// An operand shown as a variable is a register when it is one and nothing at all when it is a
1068    /// number.
1069    ///
1070    /// This is the whole of what [`Shown::Var`] is for. A canonicalisation that moves a constant
1071    /// to the right has to be able to say that the right side does not already hold one, and the
1072    /// only place that can be said is the head, since the binding is a register either way.
1073    #[test]
1074    fn an_operand_shown_as_a_variable_refuses_to_be_a_constant() {
1075        let (mut func, block) = func();
1076        let i32 = Type::int(32);
1077        let x = func.append_param(block, i32);
1078        let mut build = Builder::new(&mut func, block);
1079        let k = build.iconst(i32, 3);
1080        let sum = build.binary(Opcode::Add, x, k, Flags::default());
1081        let add = inst_of(&func, sum);
1082
1083        // Operand zero is the parameter, so it is shown, and it is shown as a register.
1084        let terms = Terms::new(&func, add, [Shown::Var, Shown::Var, Shown::Reg]);
1085        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i32", 1)));
1086        assert_eq!(terms.arg(Term::Arg(0), 0), Term::Reg(x));
1087        // Operand one is the constant, so there is no head and no rule reaches past it. Shown as
1088        // a plain register it would be `value.i32` and the rule would match.
1089        assert_eq!(terms.head(Term::Arg(1)), None);
1090        assert_eq!(Terms::new(&func, add, PLAIN).head(Term::Arg(1)), Some(("value.i32", 1)));
1091    }
1092
1093    #[test]
1094    fn a_constant_is_a_term_of_one_argument_and_has_no_operands() {
1095        let (mut func, block) = func();
1096        let mut build = Builder::new(&mut func, block);
1097        let k = build.iconst(Type::int(64), 12);
1098        let inst = inst_of(&func, k);
1099
1100        let terms = Terms::new(&func, inst, PLAIN);
1101        assert_eq!(terms.head(Term::Root), Some(("iconst.i64", 1)));
1102        assert_eq!(terms.arg(Term::Root, 0), Term::Num(12));
1103    }
1104
1105    #[test]
1106    fn an_expanded_operand_is_the_instruction_that_computed_it() {
1107        let (mut func, block) = func();
1108        let i64 = Type::int(64);
1109        // A parameter, because the point of the test is an operand that is not a constant.
1110        let y = func.append_param(block, i64);
1111        let mut build = Builder::new(&mut func, block);
1112        let x = build.iconst(i64, 1);
1113        let four = build.iconst(i64, 4);
1114        let scaled = build.binary(Opcode::Mul, y, four, Flags::default());
1115        let sum = build.binary(Opcode::Add, x, scaled, Flags::default());
1116        let add = inst_of(&func, sum);
1117
1118        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Expand, Shown::Reg]);
1119        assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
1120        assert_eq!(terms.head(Term::Arg(1)), Some(("mul.i64", 2)));
1121        assert_eq!(terms.head(Term::Deep(1, 0)), Some(("value.i64", 1)));
1122        assert_eq!(terms.arg(Term::Deep(1, 0), 0), Term::Reg(y));
1123        // The constant inside an expansion is shown as one without being asked to be.
1124        assert_eq!(terms.head(Term::Deep(1, 1)), Some(("iconst.i64", 1)));
1125        assert_eq!(terms.arg(Term::Deep(1, 1), 0), Term::Num(4));
1126    }
1127
1128    #[test]
1129    fn a_comparison_says_which_one_it_is_and_a_conversion_says_both_widths() {
1130        let (mut func, block) = func();
1131        let mut build = Builder::new(&mut func, block);
1132        let x = build.iconst(Type::int(32), 1);
1133        let y = build.iconst(Type::int(32), 2);
1134        let less = build.icmp(IntPred::Slt, x, y);
1135        let wide = build.unary(Opcode::SExt, x, Type::int(64));
1136        let narrow = build.unary(Opcode::Trunc, x, Type::int(8));
1137        let cmp = inst_of(&func, less);
1138        assert_eq!(Terms::new(&func, cmp, PLAIN).head(Term::Root), Some(("icmp_slt.i1", 2)));
1139        let sext = inst_of(&func, wide);
1140        assert_eq!(Terms::new(&func, sext, PLAIN).head(Term::Root), Some(("sext.i32.i64", 1)));
1141        let trunc = inst_of(&func, narrow);
1142        assert_eq!(Terms::new(&func, trunc, PLAIN).head(Term::Root), Some(("trunc.i32.i8", 1)));
1143    }
1144
1145    #[test]
1146    fn a_width_no_rule_is_written_at_has_no_name() {
1147        let (mut func, block) = func();
1148        let mut build = Builder::new(&mut func, block);
1149        let x = build.iconst(Type::int(128), 1);
1150        let inst = inst_of(&func, x);
1151        assert_eq!(Terms::new(&func, inst, PLAIN).head(Term::Root), None);
1152    }
1153
1154    /// An address is an integer of the machine's width to every term here, which is what lets one
1155    /// be loaded from, stored through, returned and added to by rules written about integers.
1156    #[test]
1157    fn an_address_is_an_integer_as_wide_as_the_machine_addresses() {
1158        assert_eq!(value_head(Type::PTR), Some("value.i64"));
1159        assert_eq!(load_head(Type::PTR), Some("load.i64"));
1160        assert_eq!(store_head(Type::PTR), Some("store.i64"));
1161        assert_eq!(ret_head(Type::PTR), Some("ret.i64"));
1162        // Not a constant, since nothing writes an address down as one.
1163        assert_eq!(iconst_head(Type::PTR), None);
1164    }
1165
1166    /// One bit is a width with names of its own, and they are not the four the tables hold. What
1167    /// has a name there is what a truth value is written with: a constant, the three bitwise
1168    /// operations, and the widening that turns one into a number.
1169    #[test]
1170    fn one_bit_is_a_width_with_a_name_for_what_a_truth_value_is_written_with() {
1171        let bit = Type::int(1);
1172        assert_eq!(slot(bit), None);
1173        assert_eq!(value_head(bit), Some("value.i1"));
1174        assert_eq!(iconst_head(bit), Some("iconst.i1"));
1175        assert_eq!(binary_head(Opcode::And, bit), Some("and.i1"));
1176        assert_eq!(binary_head(Opcode::Or, bit), Some("or.i1"));
1177        assert_eq!(binary_head(Opcode::Xor, bit), Some("xor.i1"));
1178        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(8)), Some("zext.i1.i8"));
1179        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(32)), Some("zext.i1.i32"));
1180        assert_eq!(convert_head(Opcode::ZExt, bit, Type::int(64)), Some("zext.i1.i64"));
1181        assert_eq!(convert_head(Opcode::Trunc, Type::int(8), bit), Some("trunc.i8.i1"));
1182        assert_eq!(convert_head(Opcode::Trunc, Type::int(64), bit), Some("trunc.i64.i1"));
1183    }
1184
1185    /// The three that reach an object of this width, which are the reason a `_Bool` in memory
1186    /// compiles at all. They are named at one bit rather than at the byte holding one because
1187    /// what the rule set has to say about them is what they do to the bit.
1188    #[test]
1189    fn the_places_a_truth_value_is_an_object_have_a_name() {
1190        let bit = Type::int(1);
1191        assert_eq!(load_head(bit), Some("load.i1"));
1192        assert_eq!(store_head(bit), Some("store.i1"));
1193        assert_eq!(ret_head(bit), Some("ret.i1"));
1194    }
1195
1196    /// Everything else at one bit has no name, which is what keeps the byte holding one a zero or
1197    /// a one: an add at this width would be an instruction that leaves something else there.
1198    #[test]
1199    fn nothing_else_at_one_bit_has_a_name() {
1200        let bit = Type::int(1);
1201        assert_eq!(binary_head(Opcode::Add, bit), None);
1202        assert_eq!(binary_head(Opcode::Shl, bit), None);
1203        // Not a sign extension either, which would be a truth value spread over every bit.
1204        assert_eq!(convert_head(Opcode::SExt, bit, Type::int(32)), None);
1205        // And not a widening to it or a narrowing from it, since neither is a conversion: the
1206        // two widths would be the same one.
1207        assert_eq!(convert_head(Opcode::ZExt, Type::int(32), bit), None);
1208        assert_eq!(convert_head(Opcode::Trunc, bit, Type::int(32)), None);
1209    }
1210
1211    /// A one bit constant is the truth value it stands for. The signed reading of a one bit
1212    /// integer turns a true into a minus one, which would put a byte of ones where every rule at
1213    /// this width expects a one.
1214    #[test]
1215    fn a_one_bit_constant_is_a_zero_or_a_one_rather_than_a_zero_or_a_minus_one() {
1216        let (mut func, block) = func();
1217        let mut build = Builder::new(&mut func, block);
1218        let bit = Type::int(1);
1219        let no = build.iconst(bit, 0);
1220        let yes = build.iconst(bit, 1);
1221        let terms = Terms::new(&func, inst_of(&func, yes), PLAIN);
1222        assert_eq!(terms.constant(no), Some(0));
1223        assert_eq!(terms.constant(yes), Some(1));
1224        assert_eq!(terms.head(Term::Root), Some(("iconst.i1", 1)));
1225        assert_eq!(terms.arg(Term::Root, 0), Term::Num(1));
1226    }
1227
1228    /// A float is a term of its own at each of the two widths the machine has instructions for.
1229    /// The same width of integer is a different term, which is what keeps a rule about one from
1230    /// ever firing on the other, and it has to be, because the two are in different register
1231    /// files.
1232    #[test]
1233    fn a_float_is_a_term_of_its_own_at_each_width_the_machine_computes_in() {
1234        let f32 = Type::float(Float::F32);
1235        let f64 = Type::float(Float::F64);
1236        assert_eq!(value_head(f32), Some("value.f32"));
1237        assert_eq!(value_head(f64), Some("value.f64"));
1238        assert_eq!(load_head(f32), Some("load.f32"));
1239        assert_eq!(store_head(f64), Some("store.f64"));
1240        assert_eq!(ret_head(f32), Some("ret.f32"));
1241        assert_eq!(binary_head(Opcode::FAdd, f32), Some("fadd.f32"));
1242        assert_eq!(binary_head(Opcode::FSub, f64), Some("fsub.f64"));
1243        assert_eq!(binary_head(Opcode::FMul, f32), Some("fmul.f32"));
1244        assert_eq!(binary_head(Opcode::FDiv, f64), Some("fdiv.f64"));
1245        // Not one of the four widths an integer rule is written at, and not a constant either,
1246        // since what a pattern binds inside an `iconst` is a number and a float is not one.
1247        assert_eq!(slot(f32), None);
1248        assert_eq!(slot(f64), None);
1249        assert_eq!(iconst_head(f64), None);
1250        // An integer add at thirty two bits is a different name from a float add at the same
1251        // width, which is the whole of what keeps the two rule sets apart.
1252        assert_ne!(binary_head(Opcode::Add, Type::int(32)), binary_head(Opcode::FAdd, f32));
1253    }
1254
1255    /// What the machine has no scalar instruction for has no name, so it is reported rather than
1256    /// lowered to something near it. A remainder is a call to `fmod` and a `long double` is on the
1257    /// x87 stack, and neither is anything a rule in this set is written about.
1258    #[test]
1259    fn a_float_operation_the_machine_lacks_has_no_name() {
1260        assert_eq!(binary_head(Opcode::FRem, Type::float(Float::F32)), None);
1261        let long = Type::float(Float::F80);
1262        assert_eq!(float_slot(long), None);
1263        assert_eq!(value_head(long), None);
1264        assert_eq!(binary_head(Opcode::FAdd, long), None);
1265        assert_eq!(ret_head(long), None);
1266    }
1267
1268    /// The quad format has a name for each of the three things that move a value and for nothing
1269    /// else, which is what the machine has: sixteen bytes into a vector register and back out, and
1270    /// no instruction that looks at them.
1271    ///
1272    /// It is not one of [`float_slot`]'s two either, and that is the half of this worth asserting.
1273    /// Every table indexed by that answer holds two names, so a third width answering it would be
1274    /// an index past the end rather than a missing rule.
1275    #[test]
1276    fn the_quad_format_is_named_for_the_moves_and_for_nothing_else() {
1277        let quad = Type::float(Float::F128);
1278        assert!(is_quad(quad));
1279        assert_eq!(float_slot(quad), None);
1280        assert!(in_vector_file(quad));
1281        assert_eq!(value_head(quad), Some("value.f128"));
1282        assert_eq!(load_head(quad), Some("load.f128"));
1283        assert_eq!(store_head(quad), Some("store.f128"));
1284        assert_eq!(ret_head(quad), Some("ret.f128"));
1285        assert_eq!(binary_head(Opcode::FAdd, quad), None);
1286        assert_eq!(fcmp_head(FloatPred::Oeq, quad), None);
1287        assert_eq!(cross_head(Opcode::FPExt, Type::float(Float::F64), quad), None);
1288        assert_eq!(cross_head(Opcode::FPTrunc, quad, Type::float(Float::F64)), None);
1289        // The eighty bit format is in neither file and has none of the three, which is what keeps
1290        // this from being a claim about every float wider than a `double`.
1291        assert!(!in_vector_file(Type::float(Float::F80)));
1292    }
1293
1294    /// A lane count is not a width, so a rule written at a width does not get to answer for a
1295    /// vector of that width. Nothing produces one yet and the day something does it should be
1296    /// reported rather than lowered to an instruction that acts on one lane of it.
1297    #[test]
1298    fn a_vector_is_not_the_width_of_its_lane() {
1299        let i32x4 = Type::vector(Type::int(32), 4);
1300        assert_eq!(slot(i32x4), None);
1301        assert_eq!(value_head(i32x4), None);
1302        assert_eq!(binary_head(Opcode::Add, i32x4), None);
1303    }
1304
1305    /// The sweep says the same thing about an instruction that looking the instruction up does,
1306    /// which is the only way it is worth anything: a list of names built beside the naming rather
1307    /// than out of it would be a second table to keep in step.
1308    #[test]
1309    fn the_names_the_sweep_finds_are_the_names_an_instruction_gets() {
1310        let (mut func, block) = func();
1311        let other = func.create_block();
1312        let mut build = Builder::new(&mut func, block);
1313        let cond = build.iconst(Type::int(1), 1);
1314        let x = build.iconst(Type::int(32), 1);
1315        let sum = build.binary(Opcode::Add, x, x, Flags::default());
1316        let branch = build.br_if(cond, other, &[], other, &[]);
1317
1318        let names = heads();
1319        for inst in [inst_of(&func, sum), inst_of(&func, x), branch] {
1320            let name = head_of(&func, inst).expect("all three have a name");
1321            let opcode = func[inst].opcode;
1322            assert!(
1323                names.contains(&(opcode, name)),
1324                "an instruction is called {name} and the sweep does not know that name"
1325            );
1326        }
1327    }
1328
1329    /// Every name is there once and belongs to one opcode. A name in the list twice would count
1330    /// twice in the coverage report, and the two instructions a name could belong to are the two
1331    /// the machine has one instruction for: an add of two numbers and an add of an address.
1332    #[test]
1333    fn a_name_is_listed_once_and_an_address_add_is_the_one_name_two_opcodes_share() {
1334        let names = heads();
1335        let mut once = names.clone();
1336        once.dedup();
1337        assert_eq!(names, once, "the sweep lists a name twice");
1338        assert!(names.contains(&(Opcode::Add, "add.i64")));
1339        assert!(names.contains(&(Opcode::PtrAdd, "add.i64")));
1340    }
1341
1342    /// A width nothing is written at contributes nothing, which is what makes the sweep safe to
1343    /// run over every type there is. These four are the widths that have no name today, and each
1344    /// is an issue rather than an oversight: one bit arithmetic, `__int128`, `long double` and a
1345    /// vector of any lane count.
1346    #[test]
1347    fn a_width_with_no_name_puts_nothing_in_the_sweep() {
1348        let named: Vec<&'static str> = heads().into_iter().map(|(_, name)| name).collect();
1349        for name in &named {
1350            assert!(!name.contains("i128"), "{name} is a width no rule is written at");
1351            assert!(!name.contains("f80"), "{name} is a width no rule is written at");
1352        }
1353        // One bit is the width with some names and not others, so it is checked from the other
1354        // side: what a truth value is written with, and nothing else. A comparison is in the list
1355        // because its result is one bit, whatever it compared.
1356        let mut bit: Vec<&'static str> =
1357            named.into_iter().filter(|name| name.ends_with(".i1")).collect();
1358        bit.sort_unstable();
1359        assert_eq!(
1360            bit,
1361            [
1362                "and.i1",
1363                "brif.i1",
1364                "fcmp_oeq.f32.i1",
1365                "fcmp_oeq.f64.i1",
1366                "fcmp_oge.f32.i1",
1367                "fcmp_oge.f64.i1",
1368                "fcmp_ogt.f32.i1",
1369                "fcmp_ogt.f64.i1",
1370                "fcmp_ole.f32.i1",
1371                "fcmp_ole.f64.i1",
1372                "fcmp_olt.f32.i1",
1373                "fcmp_olt.f64.i1",
1374                "fcmp_one.f32.i1",
1375                "fcmp_one.f64.i1",
1376                "fcmp_ord.f32.i1",
1377                "fcmp_ord.f64.i1",
1378                "fcmp_ueq.f32.i1",
1379                "fcmp_ueq.f64.i1",
1380                "fcmp_uge.f32.i1",
1381                "fcmp_uge.f64.i1",
1382                "fcmp_ugt.f32.i1",
1383                "fcmp_ugt.f64.i1",
1384                "fcmp_ule.f32.i1",
1385                "fcmp_ule.f64.i1",
1386                "fcmp_ult.f32.i1",
1387                "fcmp_ult.f64.i1",
1388                "fcmp_une.f32.i1",
1389                "fcmp_une.f64.i1",
1390                "fcmp_uno.f32.i1",
1391                "fcmp_uno.f64.i1",
1392                "icmp_eq.i1",
1393                "icmp_ne.i1",
1394                "icmp_sge.i1",
1395                "icmp_sgt.i1",
1396                "icmp_sle.i1",
1397                "icmp_slt.i1",
1398                "icmp_uge.i1",
1399                "icmp_ugt.i1",
1400                "icmp_ule.i1",
1401                "icmp_ult.i1",
1402                "iconst.i1",
1403                "load.i1",
1404                "or.i1",
1405                "ret.i1",
1406                "store.i1",
1407                "trunc.i16.i1",
1408                "trunc.i32.i1",
1409                "trunc.i64.i1",
1410                "trunc.i8.i1",
1411                "xor.i1",
1412            ]
1413        );
1414    }
1415
1416    /// Address arithmetic is named as the add it is, which is what puts it in reach of every rule
1417    /// written about one, including the two below that fold it into an address.
1418    #[test]
1419    fn address_arithmetic_is_an_add_at_the_address_width() {
1420        let (mut func, block) = func();
1421        let base = func.append_param(block, Type::PTR);
1422        let mut build = Builder::new(&mut func, block);
1423        let step = build.iconst(Type::int(64), 4);
1424        let args = func.push_values(&[base, step]);
1425        let next = Builder::new(&mut func, block)
1426            .value(crate::InstData { args, ..crate::InstData::new(Opcode::PtrAdd) }, Type::PTR);
1427        let inst = inst_of(&func, next);
1428
1429        let terms = Terms::new(&func, inst, [Shown::Reg, Shown::Const, Shown::Reg]);
1430        assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
1431        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i64", 1)));
1432        assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i64", 1)));
1433        assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(4));
1434    }
1435}