Skip to main content

rucc_opt/
fold.rs

1//! Constant folding: an instruction whose operands are all constants becomes a constant.
2//!
3//! The smallest transformation there is, and the one the rest of the middle end leans on. Every
4//! later pass produces constants where the source had none, and none of them should have to
5//! evaluate the arithmetic itself.
6//!
7//! It is worth having before any of them because the lowering walk produces constant arithmetic
8//! that nothing in the C asked for. The usual arithmetic conversions widen a literal to the type
9//! of the other operand, so `long y; y + 7` lowers to a 32 bit constant, a `sext` of it and an
10//! add, and nothing downstream can see that the operand of the add is a number. On x86-64 that
11//! costs two instructions and a register on every operation between a wide integer and a
12//! literal, which is most address arithmetic and most loop bounds in real code. That is issue
13//! 378.
14//!
15//! The bit counting instructions are here for a related reason. Nothing in the backend selects an
16//! instruction for any of them yet, so each one that survives to the end becomes the twenty odd
17//! instructions of the software expansion in `rucc_codegen::expand`, inlined at the site. A
18//! `__builtin_clzll` on a value the compiler can already see is the worst version of that: the
19//! answer is a number between nought and sixty four and the code that computes it is the largest
20//! thing in the function. Folding it costs one arm here. That is part of issue 310.
21//!
22//! # How it rewrites
23//!
24//! In place. An instruction that folds keeps its result value and becomes an `iconst`, because
25//! the value it produced already has the right type and every use of it is already correct. So
26//! there is no rewriting of uses, no new value, and nothing for a later pass to have to know
27//! about. What is left behind is the old operand, now used by nothing, which costs nothing in
28//! the output because the backend materializes a constant where it is wanted rather than where
29//! the IR wrote it, and which dead code elimination will take out of the printed IR when there
30//! is one.
31//!
32//! # What it does not fold
33//!
34//! Not the divides and the remainders. Both have two cases the language leaves undefined, a zero
35//! divisor and the most negative value divided by minus one, and both want guarding rather than
36//! evaluating. They belong with the strength reduction that turns a division by a constant into
37//! a multiply, which is where somebody looking for division arithmetic will look.
38//!
39//! Not floating point arithmetic. Folding it means deciding what rounding mode to fold under and
40//! what to do about a signalling NaN, and `rucc_base::float` has the arithmetic but the decision
41//! about the environment belongs with the rest of the floating point work rather than in the first
42//! pass.
43//!
44//! Negation is folded, and is inside that boundary rather than an exception to it. 754 says a
45//! negation flips the sign bit and copies every other bit, for every input including a NaN and a
46//! zero, so it is exact, it raises nothing and it never consults the rounding mode: there is no
47//! decision about the environment in it to get wrong. The reason to bother is that C has no
48//! negative floating constant. Every one of them is a unary minus applied to a positive one, so
49//! `-1.0` arrives as an `fneg` of an `fconst`, and without this the back end makes a constant, a
50//! mask and three moves through a general register out of what should be one load. That is every
51//! negative floating literal in every program, and it is issue 1427.
52//!
53//! A bitcast of a constant is folded for the same reason and pays for the same kind of code. It is
54//! the same bits read as another type of the same width, so there is nothing to decide about it
55//! either, and what it unblocks is `fabs` and `copysign` of a constant: neither is a call, the
56//! front end lowers both to a mask over the bits, and without this the mask and the two bitcasts
57//! around it survive to the back end computing a number the compiler already has.
58//!
59//! A conversion from floating point to an integer is folded, and is inside that boundary rather
60//! than an exception to it. C says the conversion discards the fractional part, so the rounding is
61//! the language's rather than the environment's and nothing anybody sets at run time reaches it.
62//! What is left is a value whose truncation does not fit the destination type, and a NaN, and both
63//! of those are undefined rather than a number: `rucc_base::float::Float::to_integer` reports each
64//! as `Status::INVALID` and neither folds, which is the rule below for an add that overflows under
65//! `nsw` applied to the same kind of program. That is issue 1357.
66//!
67//! Not an operation that overflows under `nsw` or `nuw`. The result there is poison, so any
68//! answer would be a valid refinement, and quietly picking the wrapping one hides a program that
69//! has stepped outside the language from the sanitizer that should be reporting it.
70//!
71//! Not floating point comparisons, for the reason above and one more: an ordered predicate and an
72//! unordered one differ only on a NaN, so the answer is the whole of what makes them two
73//! predicates, and evaluating it is the floating point decision rather than a step around it.
74//!
75//! Integer comparisons are folded, and were not until issue 352 was closed. An `icmp` produces an
76//! `i1`, and while nothing lowered one that was left standing on its own, folding one would have
77//! turned working code into code that does not build. There is now a rule for a one bit constant
78//! and one for a byte holding it, so the constant this leaves behind lowers wherever the
79//! comparison did.
80
81use rucc_base::float::{Float, Status};
82use rucc_ir::{Block, Def, Extra, Flags, Func, Imm, Inst, InstData, IntPred, Opcode, Type, Value};
83
84use crate::{Analyses, Analysis, Fuel, Pass, Preserved, Stats};
85
86/// Recorded once for each instruction that became a constant.
87const FOLDED: &str = "instruction with constant operands folded to a constant";
88
89/// Recorded for an instruction that would have folded if there had been fuel for it.
90///
91/// Not a missed optimization in the ordinary sense, since the fuel is a person deliberately
92/// stopping the pass. It is here because it is the number a bisection is searching for: the count
93/// of sites past the cut is how far there is left to go.
94const NO_FUEL: &str = "instruction not folded, the pass ran out of fuel";
95
96/// The pass. It holds nothing, because folding needs to know nothing beyond the instruction.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct Fold;
99
100impl Pass for Fold {
101    fn name(&self) -> &'static str {
102        "fold"
103    }
104
105    fn describe(&self) -> &'static str {
106        "an instruction whose operands are all constants becomes a constant"
107    }
108
109    fn preserves(&self) -> Preserved {
110        // An instruction becomes a constant where it stands. No block moves, no edge moves,
111        // and a terminator is not one of the instructions this folds, so every analysis in the
112        // cache is about the same graph afterwards as it was before. Not the liveness, though:
113        // the operands the folded instruction read are read by nobody now, and a value whose
114        // last reader went is live over less of the function than it was.
115        Preserved::ALL.without(Analysis::Liveness)
116    }
117
118    fn run(&self, func: &mut Func, _an: &mut Analyses, fuel: &mut Fuel) -> Stats {
119        fold_in(func, fuel)
120    }
121}
122
123/// The whole of the pass, without the analysis cache it does not read.
124///
125/// Apart so that [`crate::ipcp`] can fold a function it has just put a constant into. The
126/// arithmetic a constant parameter enables is what makes that propagation reach a second level, and
127/// running this rather than evaluating it there is what keeps the arithmetic written down once.
128pub(crate) fn fold_in(func: &mut Func, fuel: &mut Fuel) -> Stats {
129    let blocks: Vec<Block> = func.blocks().collect();
130    let mut stats = Stats::new();
131    for block in blocks {
132        let insts: Vec<Inst> = func.insts(block).collect();
133        for inst in insts {
134            let Some(folded) = evaluate(func, inst) else { continue };
135            if !fuel.take() {
136                // Out of fuel, which is a request to stop transforming rather than to stop
137                // looking. Continuing the walk costs nothing and keeps the count of what
138                // could have been folded the same at every fuel setting, which is what makes
139                // a bisection over it monotonic.
140                stats.missed(NO_FUEL);
141                continue;
142            }
143            let ty = func[result_of(func, inst)].ty;
144            let at = func.add_imm(folded);
145            let data = &mut func[inst];
146            // Which constant instruction holds the answer is the result type's question and
147            // not the folded instruction's. An `fneg` and a bitcast out of an integer both
148            // answer in a floating point type and the rest of what folds here answers in an
149            // integer one, and an immediate is the same bits either way.
150            data.opcode = if ty.is_int() { Opcode::IConst } else { Opcode::FConst };
151            data.flags = Flags::NONE;
152            data.args = rucc_ir::ValueList::EMPTY;
153            data.extra = Extra::Imm(at);
154            stats.optimized(FOLDED);
155        }
156    }
157    stats
158}
159
160/// The single result of an instruction that folded.
161fn result_of(func: &Func, inst: Inst) -> Value {
162    func[inst].results().next().expect("an instruction that folds produces a value")
163}
164
165/// What this instruction evaluates to, if it evaluates to anything.
166///
167/// `None` covers every reason not to fold and does not distinguish between them, because the
168/// answer to all of them is the same: leave the instruction alone.
169fn evaluate(func: &Func, inst: Inst) -> Option<Imm> {
170    let data = &func[inst];
171    if data.results != 1 {
172        return None;
173    }
174    let result = data.results().next()?;
175    let ty = func[result].ty;
176    // A vector constant is a `splat` rather than an `iconst` or an `fconst`, so a vector fold
177    // would have to build a different instruction and would have to be right about the lane
178    // count as well.
179    if !ty.is_scalar() {
180        return None;
181    }
182    let args = &func[data.args];
183    // The two that are the bits and nothing else, and the only two here whose answer can have a
184    // floating point type. They are above the gate below rather than inside the match under it
185    // because that gate is what keeps the rest of this file about integers.
186    match data.opcode {
187        Opcode::FNeg => return negated(func, *args.first()?, ty),
188        Opcode::Bitcast => return reinterpreted(func, *args.first()?, ty),
189        _ => {}
190    }
191    if !ty.is_int() {
192        return None;
193    }
194    match data.opcode {
195        Opcode::FPToSI | Opcode::FPToUI => {
196            let value = floating(func, *args.first()?)?;
197            to_integer(value, ty, data.opcode == Opcode::FPToSI)
198        }
199        _ => arithmetic(data, args, ty, &|value| constant(func, value)),
200    }
201}
202
203/// What an instruction of integer arithmetic works out to, given a way to read each operand as a
204/// constant.
205///
206/// The way to read an operand is the caller's, because this pass wants an operand that is already
207/// a constant and nothing more, while [`evaluated`] wants to go on looking underneath one that is
208/// not. Both of them want the arithmetic itself to be this one, so that the two cannot disagree
209/// about what an instruction answers.
210fn arithmetic(
211    data: &InstData,
212    args: &[Value],
213    ty: Type,
214    operand: &dyn Fn(Value) -> Option<(Imm, Type)>,
215) -> Option<Imm> {
216    match data.opcode {
217        Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
218            let (value, from) = operand(*args.first()?)?;
219            Some(convert(data.opcode, value, from, ty))
220        }
221        Opcode::Shl | Opcode::LShr | Opcode::AShr => {
222            let (value, from) = operand(*args.first()?)?;
223            let (count, count_ty) = operand(*args.get(1)?)?;
224            shift(data.opcode, value, from, count, count_ty, ty, data.flags)
225        }
226        Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::And | Opcode::Or | Opcode::Xor => {
227            let (lhs, lhs_ty) = operand(*args.first()?)?;
228            let (rhs, _) = operand(*args.get(1)?)?;
229            binary(data.opcode, lhs, rhs, lhs_ty, ty, data.flags)
230        }
231        Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
232            let (value, from) = operand(*args.first()?)?;
233            count(data.opcode, value, from, ty)
234        }
235        Opcode::ICmp => {
236            let Extra::IntPred(pred) = data.extra else { return None };
237            let (lhs, from) = operand(*args.first()?)?;
238            let (rhs, _) = operand(*args.get(1)?)?;
239            Some(Imm::int(i128::from(compare(pred, lhs, rhs, from)), ty))
240        }
241        // A select between two constants that are the same number is that number whichever way
242        // the condition goes. Unrolling makes these, when both arms worked out something from a
243        // counter that is now a constant and came to the same answer, and each arm's answer is
244        // its own constant instruction, so nothing that compares values sees they are equal.
245        Opcode::Select => {
246            let (then, _) = operand(*args.get(1)?)?;
247            let (other, _) = operand(*args.get(2)?)?;
248            (then.signed(ty) == other.signed(ty)).then_some(then)
249        }
250        _ => None,
251    }
252}
253
254/// The constant this value works out to, looking through as many as `depth` instructions of
255/// integer arithmetic over constants.
256///
257/// For a pass that runs before this one has had the chance to write the answer down as a constant,
258/// which is [`crate::libcall`]: it runs over the module before any function pass has started, so
259/// an index the source wrote as `x & 3` with `x` known is still an `and` of two constants when it
260/// looks. Nothing here changes the function, and the arithmetic is [`arithmetic`], so the answer
261/// is the one this pass would have written later.
262pub(crate) fn evaluated(func: &Func, value: Value, depth: u32) -> Option<(Imm, Type)> {
263    if let Some(found) = constant(func, value) {
264        return Some(found);
265    }
266    let next = depth.checked_sub(1)?;
267    let Def::Result { inst, .. } = func[value].def else { return None };
268    let data = &func[inst];
269    let ty = func[value].ty;
270    if data.results != 1 || !ty.is_int() || !ty.is_scalar() {
271        return None;
272    }
273    let found = arithmetic(data, &func[data.args], ty, &|arg| evaluated(func, arg, next))?;
274    Some((found, ty))
275}
276
277/// The bits a value holds, if it is a constant of either kind.
278///
279/// Both kinds, because the two rewrites above this are about the bits and do not care which of
280/// them they were written as. A constant of either is one instruction with one immediate, and an
281/// immediate is the bits.
282fn bits_of(func: &Func, value: Value) -> Option<u128> {
283    let Def::Result { inst, .. } = func[value].def else { return None };
284    let data = &func[inst];
285    if !matches!(data.opcode, Opcode::IConst | Opcode::FConst) {
286        return None;
287    }
288    let Extra::Imm(at) = data.extra else { return None };
289    Some(func[at].bits())
290}
291
292/// A negation of a floating point constant, which is that constant with its sign bit flipped.
293///
294/// This is the one piece of floating point arithmetic that folds, and it is inside the boundary
295/// the file header draws rather than an exception to it. Negation is not arithmetic in the sense
296/// that boundary is about: 754 says it flips the sign bit and copies every other bit, for every
297/// input including a NaN and a zero, so it is exact, it raises nothing and it never consults the
298/// rounding mode. There is no decision about the environment to get wrong.
299///
300/// The reason to bother is that C has no negative floating constant. Every one of them is a unary
301/// minus applied to a positive one, so `-1.0` arrives here as an `fneg` of an `fconst` and stays
302/// that way, and what the back end makes of it is a constant, a mask and three moves through a
303/// general register where one load would do. That is every negative floating literal in every
304/// program, and it is issue 1427.
305///
306/// The sign bit is the top bit of the value and not of the object it is stored in. An `f80` is
307/// eighty bits of value in a hundred and twenty eight of storage, and [`Type::bits`] answers
308/// eighty for it, which is the bit this has to flip.
309fn negated(func: &Func, operand: Value, ty: Type) -> Option<Imm> {
310    if !ty.is_float() {
311        return None;
312    }
313    let bits = bits_of(func, operand)?;
314    Some(Imm::from_bits(bits ^ 1u128 << (ty.bits() - 1)))
315}
316
317/// A bitcast of a constant, which is the same bits read as another type of the same width.
318///
319/// It folds in both directions, and the one that pays is out of an integer, because that is what
320/// `fabs` and `copysign` leave behind. Neither is a call: the front end lowers both to a mask over
321/// the bits, so `fabs (1.0)` is a bitcast of an `and` of a bitcast, and without this the three
322/// survive to the back end and compute a number the compiler already has.
323///
324/// The widths are checked rather than assumed. The verifier requires them to match and a fold that
325/// quietly widened or narrowed a constant would be a wrong answer rather than a refused one, which
326/// is not a thing to leave to another pass being right.
327fn reinterpreted(func: &Func, operand: Value, ty: Type) -> Option<Imm> {
328    let from = func[operand].ty;
329    if !from.is_scalar() || from.bits() != ty.bits() {
330        return None;
331    }
332    let bits = bits_of(func, operand)?;
333    Some(Imm::from_bits(bits))
334}
335
336/// The constant this value is, with the type it has, if it is one.
337///
338/// Shared with [`crate::simplify_cfg`], which asks the same question about the condition of a
339/// branch. Asking it in two places would be two answers about what a constant is.
340pub(crate) fn constant(func: &Func, value: Value) -> Option<(Imm, Type)> {
341    let Def::Result { inst, .. } = func[value].def else { return None };
342    if func[inst].opcode != Opcode::IConst {
343        return None;
344    }
345    let Extra::Imm(at) = func[inst].extra else { return None };
346    let ty = func[value].ty;
347    ty.is_int().then(|| (func[at], ty))
348}
349
350/// The floating point constant this value is, read in the format its own type gives it.
351///
352/// An `fconst` stores the bits and the type says how to read them, which is why this is one
353/// function and not a pair of them: the bits of an `f80` and the bits of an `f128` are the same
354/// hundred and twenty eight bits and mean different numbers.
355fn floating(func: &Func, value: Value) -> Option<Float> {
356    let Def::Result { inst, .. } = func[value].def else { return None };
357    if func[inst].opcode != Opcode::FConst {
358        return None;
359    }
360    let Extra::Imm(at) = func[inst].extra else { return None };
361    let format = func[value].ty.format()?.encoding();
362    Some(Float::from_bits(format, func[at].bits()))
363}
364
365/// A conversion of a floating point constant to an integer, and nothing when C does not say what
366/// the answer is.
367///
368/// The two undefined cases are a number whose truncation is outside the destination type and a
369/// NaN, and `to_integer` reports both as [`Status::INVALID`] rather than answering. Folding either
370/// would be picking one refinement of poison and writing it into the program, which is what this
371/// pass declines to do for an add that overflows under `nsw` and declines to do here for the same
372/// reason.
373fn to_integer(value: Float, to: Type, signed: bool) -> Option<Imm> {
374    let (number, status) = value.to_integer(to.bits(), signed);
375    (!status.has(Status::INVALID)).then(|| Imm::int(number, to))
376}
377
378/// A widening or a narrowing of a constant.
379pub(crate) fn convert(opcode: Opcode, value: Imm, from: Type, to: Type) -> Imm {
380    match opcode {
381        // Truncation is the masking that `Imm::int` does anyway, and sign extension is reading
382        // the value as signed at its own width and storing it at the wider one.
383        Opcode::Trunc | Opcode::SExt => Imm::int(value.signed(from), to),
384        // Zero extension reads the same bits as unsigned, which for a width below 128 is a
385        // non-negative number and survives the cast to the signed type `Imm::int` takes.
386        _ => Imm::int(value.unsigned() as i128, to),
387    }
388}
389
390/// A shift of a constant by a constant.
391///
392/// `None` when the count is not one the language defines, which is a count at or above the width
393/// of the value. The result there is poison and folding it would be picking an answer for a
394/// program that asked for none.
395fn shift(
396    opcode: Opcode,
397    value: Imm,
398    from: Type,
399    count: Imm,
400    count_ty: Type,
401    to: Type,
402    flags: Flags,
403) -> Option<Imm> {
404    let by = count.unsigned();
405    if by >= u128::from(to.bits()) || count.signed(count_ty) < 0 {
406        return None;
407    }
408    let by = by as u32;
409    let exact = match opcode {
410        Opcode::Shl => value.signed(from).checked_shl(by)?,
411        // A logical shift right is on the bits rather than on the number, so it reads unsigned
412        // and the cast back cannot lose anything: the value has at most `from.bits()` bits set
413        // and shifting right sets none.
414        Opcode::LShr => (value.unsigned() >> by) as i128,
415        _ => value.signed(from) >> by,
416    };
417    if opcode == Opcode::Shl && overflowed(exact, to, flags) {
418        return None;
419    }
420    Some(Imm::int(exact, to))
421}
422
423/// An arithmetic or bitwise operation on two constants.
424fn binary(opcode: Opcode, lhs: Imm, rhs: Imm, from: Type, to: Type, flags: Flags) -> Option<Imm> {
425    let (a, b) = (lhs.signed(from), rhs.signed(from));
426    let exact = match opcode {
427        // The bitwise three cannot overflow and are the same operation whichever way the
428        // operands are read, so they take the signed reading and are done.
429        Opcode::And => a & b,
430        Opcode::Or => a | b,
431        Opcode::Xor => a ^ b,
432        // The arithmetic three are computed at 128 bits and then asked whether they fit. A type
433        // of 128 bits is the one case where the checked form is doing real work rather than
434        // being a formality, and it is why these are checked rather than wrapping.
435        Opcode::Add => a.checked_add(b)?,
436        Opcode::Sub => a.checked_sub(b)?,
437        _ => a.checked_mul(b)?,
438    };
439    if overflowed(exact, to, flags) {
440        return None;
441    }
442    Some(Imm::int(exact, to))
443}
444
445/// What a comparison of two constants comes out as.
446///
447/// Shared with [`crate::simplify_cfg`], which asks the same question about the condition of a
448/// branch it is deciding the direction of. Two answers about what `slt` means would be one too
449/// many, and the two places would not be checked against each other by anything.
450///
451/// The type is the one the operands have rather than the `i1` the answer has, since that is the
452/// width the comparison is at and the only thing the reading depends on. The two equalities are
453/// the same question whichever way the bits are read, so they compare the immediates directly:
454/// an immediate holds its value in exactly the width of its type, which is what makes that
455/// equality the equality on the numbers.
456pub(crate) fn compare(pred: IntPred, lhs: Imm, rhs: Imm, ty: Type) -> bool {
457    match pred {
458        IntPred::Eq => lhs == rhs,
459        IntPred::Ne => lhs != rhs,
460        IntPred::Slt => lhs.signed(ty) < rhs.signed(ty),
461        IntPred::Sle => lhs.signed(ty) <= rhs.signed(ty),
462        IntPred::Sgt => lhs.signed(ty) > rhs.signed(ty),
463        IntPred::Sge => lhs.signed(ty) >= rhs.signed(ty),
464        IntPred::Ult => lhs.unsigned() < rhs.unsigned(),
465        IntPred::Ule => lhs.unsigned() <= rhs.unsigned(),
466        IntPred::Ugt => lhs.unsigned() > rhs.unsigned(),
467        IntPred::Uge => lhs.unsigned() >= rhs.unsigned(),
468    }
469}
470
471/// One of the five bit operations on a constant.
472///
473/// All five are on the bits rather than on the number, so all five read the value unsigned. An
474/// immediate is stored with everything above its own width cleared, so the bits of a value of a
475/// narrow type are already in the low end of a 128 bit word with zeroes above them, and the whole
476/// of the work here is putting the answer back at the width it was asked at.
477///
478/// The two searches answer the width for a zero argument. C leaves `__builtin_clz(0)` and
479/// `__builtin_ctz(0)` undefined so nothing is entitled to that answer, but it is the answer the
480/// software expansion in `rucc_codegen::expand` gives and `__builtin_ffs` is built on top of it, so
481/// folding to anything else here would make the same program answer two different things depending
482/// on whether the argument was visible. That is a worse outcome than either answer on its own.
483///
484/// A byte swap of a width that is not a whole number of bytes is left alone, which is what the
485/// expansion does with one too. The verifier does not allow one and quietly reversing something
486/// else would be worse than the instruction surviving to a selector that says it has no rule.
487fn count(opcode: Opcode, value: Imm, from: Type, to: Type) -> Option<Imm> {
488    let width = from.bits();
489    if width == 0 || width > 128 {
490        return None;
491    }
492    // The bits of the word that are above the value's own type, which is how far a whole word
493    // answer has to come back down. Both ends of the range above are ruled out for it: a shift by
494    // the width of the word is not defined and a width of nought has no bits to answer about.
495    let spare = 128 - width;
496    let bits = value.unsigned();
497    let answer = match opcode {
498        Opcode::Ctpop => i128::from(bits.count_ones()),
499        // The zeroes above the type are counted by the word and are not the value's, so they come
500        // off. For a zero value that leaves the width, which is the answer wanted.
501        Opcode::Ctlz => i128::from(bits.leading_zeros() - spare),
502        // Trailing zeroes need no correction because the zeroes above the type are above every
503        // set bit, except for a zero value, where the word answers 128 and the width is wanted.
504        Opcode::Cttz => i128::from(bits.trailing_zeros().min(width)),
505        Opcode::Bswap if width % 8 == 0 => (bits.swap_bytes() >> spare) as i128,
506        Opcode::Bitreverse => (bits.reverse_bits() >> spare) as i128,
507        _ => return None,
508    };
509    Some(Imm::int(answer, to))
510}
511
512/// Whether storing `exact` at `to` would lose something the flags promised would not happen.
513///
514/// An operation with neither flag wraps, and wrapping is defined, so the answer there is no
515/// however far outside the type the exact result is.
516fn overflowed(exact: i128, to: Type, flags: Flags) -> bool {
517    let stored = Imm::int(exact, to);
518    if flags.contains(Flags::NSW) && stored.signed(to) != exact {
519        return true;
520    }
521    flags.contains(Flags::NUW) && (exact < 0 || stored.unsigned() != exact as u128)
522}
523
524#[cfg(test)]
525mod tests {
526    use rucc_base::Interner;
527    use rucc_base::float::Format;
528    use rucc_ir::{
529        Block, Builder, Extra, Flags, Float, Func, IntPred, Module, Opcode, Signature, Type, Value,
530    };
531    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
532
533    use crate::stats::Kind;
534    use crate::{Fuel, Pass, fold::Fold};
535
536    /// A function with one block, ready to have instructions appended to it.
537    fn blank() -> (Interner, Func, Block) {
538        let mut names = Interner::new();
539        let name = names.intern("f");
540        let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(64)]));
541        let block = func.create_block();
542        (names, func, block)
543    }
544
545    /// Runs the pass over the function with as much fuel as it wants, and says whether it
546    /// rewrote anything.
547    fn fold(func: &mut Func) -> bool {
548        Fold.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited()).changed()
549    }
550
551    /// An `fconst` of the number this text spells, in the format the type gives it.
552    ///
553    /// Through `rucc_base::float` rather than through the host's `f64`, for the reason that module
554    /// exists: the bits a literal means are the target's answer and not the machine running the
555    /// test's.
556    fn number(build: &mut Builder<'_>, text: &str, ty: Type) -> Value {
557        let format = ty.format().expect("a floating point type").encoding();
558        let (value, _) = super::Float::parse(text, format).expect("a number");
559        build.fconst(ty, value.to_bits())
560    }
561
562    /// The constant a value now holds, or `None` if it is not one.
563    fn value_of(func: &Func, value: Value, ty: Type) -> Option<i128> {
564        let rucc_ir::Def::Result { inst, .. } = func[value].def else { return None };
565        if func[inst].opcode != Opcode::IConst {
566            return None;
567        }
568        let Extra::Imm(at) = func[inst].extra else { return None };
569        Some(func[at].signed(ty))
570    }
571
572    /// The bits a value now holds, or `None` if it is not a floating point constant.
573    fn float_bits(func: &Func, value: Value) -> Option<u128> {
574        let rucc_ir::Def::Result { inst, .. } = func[value].def else { return None };
575        if func[inst].opcode != Opcode::FConst {
576            return None;
577        }
578        let Extra::Imm(at) = func[inst].extra else { return None };
579        Some(func[at].bits())
580    }
581
582    /// C has no negative floating constant, so `-1.0` is a unary minus on a positive one and
583    /// arrives here as two instructions. This is the fold that makes it one.
584    #[test]
585    fn a_negated_floating_constant_becomes_a_constant() {
586        let (_, mut func, block) = blank();
587        let ty = Type::float(Float::F64);
588        let mut build = Builder::new(&mut func, block);
589        let one = number(&mut build, "1.0", ty);
590        let minus = build.unary(Opcode::FNeg, one, ty);
591        build.ret(&[minus]);
592        assert!(fold(&mut func));
593        assert_eq!(float_bits(&func, minus), Some(0xbff0_0000_0000_0000));
594    }
595
596    /// Negation is the sign bit and nothing else, which is what lets it fold at all, and a
597    /// negative zero is where that shows: the value is equal to a positive zero and the bits are
598    /// not, so anything that went through a comparison would give the wrong answer here.
599    #[test]
600    fn a_negated_zero_keeps_its_sign_bit() {
601        let (_, mut func, block) = blank();
602        let ty = Type::float(Float::F64);
603        let mut build = Builder::new(&mut func, block);
604        let zero = number(&mut build, "0.0", ty);
605        let minus = build.unary(Opcode::FNeg, zero, ty);
606        build.ret(&[minus]);
607        assert!(fold(&mut func));
608        assert_eq!(float_bits(&func, minus), Some(1 << 63));
609    }
610
611    /// The same for a NaN, whose payload goes through untouched. 754 says negation copies every
612    /// bit but the sign for every input, and a NaN is the input where a compiler that quietly did
613    /// arithmetic instead would be caught.
614    #[test]
615    fn a_negated_nan_keeps_its_payload() {
616        let (_, mut func, block) = blank();
617        let ty = Type::float(Float::F64);
618        let mut build = Builder::new(&mut func, block);
619        let nan = build.fconst(ty, 0x7ff8_0000_dead_beef);
620        let minus = build.unary(Opcode::FNeg, nan, ty);
621        build.ret(&[minus]);
622        assert!(fold(&mut func));
623        assert_eq!(float_bits(&func, minus), Some(0xfff8_0000_dead_beef));
624    }
625
626    /// The sign bit of an `f80` is the top bit of the eighty the value has and not of the hundred
627    /// and twenty eight the object is stored in, which is the one place this could be written
628    /// wrong and give a number nobody asked for.
629    #[test]
630    fn the_sign_bit_of_an_x87_value_is_the_top_bit_of_its_width() {
631        let (_, mut func, block) = blank();
632        let ty = Type::float(Float::F80);
633        let mut build = Builder::new(&mut func, block);
634        let one = number(&mut build, "1.0", ty);
635        let minus = build.unary(Opcode::FNeg, one, ty);
636        build.ret(&[minus]);
637        assert!(fold(&mut func));
638        let bits = float_bits(&func, minus).expect("a constant");
639        assert_eq!(bits >> 79 & 1, 1, "the sign bit is set");
640        assert_eq!(bits >> 80, 0, "nothing above the value is touched");
641    }
642
643    /// A bitcast of a constant is the same bits read as another type, which is what `fabs` of a
644    /// constant needs: the front end lowers it to a mask over the bits rather than to a call, so
645    /// folding it away is three instructions rather than one.
646    #[test]
647    fn a_bitcast_of_a_constant_is_the_same_bits() {
648        let (_, mut func, block) = blank();
649        let ty = Type::float(Float::F64);
650        let bits = Type::int(64);
651        let mut build = Builder::new(&mut func, block);
652        let value = number(&mut build, "-3.5", ty);
653        let number = build.unary(Opcode::Bitcast, value, bits);
654        let mask = build.iconst(bits, i128::from(i64::MAX));
655        let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
656        let back = build.unary(Opcode::Bitcast, cleared, ty);
657        build.ret(&[back]);
658        assert!(fold(&mut func));
659        assert_eq!(float_bits(&func, back), Some(0x400c_0000_0000_0000));
660    }
661
662    /// A bitcast whose operand is not a constant is left alone, which is the case nearly every
663    /// bitcast in a real function is.
664    #[test]
665    fn a_bitcast_of_something_that_is_not_a_constant_is_left_alone() {
666        let mut names = Interner::new();
667        let name = names.intern("f");
668        let ty = Type::float(Float::F64);
669        let signature = Signature::new().with_params(&[ty]).with_returns(&[Type::int(64)]);
670        let mut func = Func::new(name, signature);
671        let block = func.create_block();
672        let x = func.append_param(block, ty);
673        let mut build = Builder::new(&mut func, block);
674        let number = build.unary(Opcode::Bitcast, x, Type::int(64));
675        build.ret(&[number]);
676        assert!(!fold(&mut func));
677        assert_eq!(value_of(&func, number, Type::int(64)), None);
678    }
679
680    #[test]
681    fn a_widened_constant_becomes_a_constant_of_the_wider_type() {
682        let (_, mut func, block) = blank();
683        let mut build = Builder::new(&mut func, block);
684        let narrow = build.iconst(Type::int(32), 7);
685        let wide = build.unary(Opcode::SExt, narrow, Type::int(64));
686        build.ret(&[wide]);
687        assert!(fold(&mut func));
688        assert_eq!(value_of(&func, wide, Type::int(64)), Some(7));
689    }
690
691    #[test]
692    fn sign_extension_copies_the_sign_and_zero_extension_does_not() {
693        for (opcode, expected) in [(Opcode::SExt, -1_i128), (Opcode::ZExt, 0xffff_ffff)] {
694            let (_, mut func, block) = blank();
695            let mut build = Builder::new(&mut func, block);
696            let narrow = build.iconst(Type::int(32), -1);
697            let wide = build.unary(opcode, narrow, Type::int(64));
698            build.ret(&[wide]);
699            assert!(fold(&mut func));
700            assert_eq!(value_of(&func, wide, Type::int(64)), Some(expected), "{opcode:?}");
701        }
702    }
703
704    #[test]
705    fn truncation_keeps_the_low_bits_and_reads_them_at_the_narrow_width() {
706        let (_, mut func, block) = blank();
707        let mut build = Builder::new(&mut func, block);
708        let wide = build.iconst(Type::int(32), 0x1234_5680);
709        let narrow = build.unary(Opcode::Trunc, wide, Type::int(8));
710        build.ret(&[narrow]);
711        assert!(fold(&mut func));
712        assert_eq!(value_of(&func, narrow, Type::int(8)), Some(-128));
713    }
714
715    #[test]
716    fn the_arithmetic_and_the_bitwise_operations_are_evaluated() {
717        let cases = [
718            (Opcode::Add, 6_i128, 7_i128, 13_i128),
719            (Opcode::Sub, 6, 7, -1),
720            (Opcode::Mul, 6, 7, 42),
721            (Opcode::And, 0b1100, 0b1010, 0b1000),
722            (Opcode::Or, 0b1100, 0b1010, 0b1110),
723            (Opcode::Xor, 0b1100, 0b1010, 0b0110),
724        ];
725        for (opcode, a, b, want) in cases {
726            let (_, mut func, block) = blank();
727            let mut build = Builder::new(&mut func, block);
728            let lhs = build.iconst(Type::int(64), a);
729            let rhs = build.iconst(Type::int(64), b);
730            let out = build.binary(opcode, lhs, rhs, Flags::NONE);
731            build.ret(&[out]);
732            assert!(fold(&mut func), "{opcode:?}");
733            assert_eq!(value_of(&func, out, Type::int(64)), Some(want), "{opcode:?}");
734        }
735    }
736
737    /// A select of two constants of the same number, under a condition nothing knows, is that
738    /// number, and one of two different numbers is left for the back end to choose between.
739    #[test]
740    fn a_select_between_two_equal_constants_is_that_constant() {
741        for (other, folds) in [(2_i128, true), (3, false)] {
742            let mut names = Interner::new();
743            let signature = Signature::new().with_params(&[Type::int(32)]);
744            let mut func = Func::new(names.intern("f"), signature.with_returns(&[Type::int(32)]));
745            let block = func.create_block();
746            let x = func.append_param(block, Type::int(32));
747            let mut build = Builder::new(&mut func, block);
748            let zero = build.iconst(Type::int(32), 0);
749            let test = build.icmp(IntPred::Slt, x, zero);
750            let then = build.iconst(Type::int(32), 2);
751            let other = build.iconst(Type::int(32), other);
752            let out = build.select(test, then, other);
753            build.ret(&[out]);
754            assert_eq!(fold(&mut func), folds);
755            let want = folds.then_some(2);
756            assert_eq!(value_of(&func, out, Type::int(32)), want);
757        }
758    }
759
760    #[test]
761    fn the_three_shifts_are_evaluated_and_the_two_right_ones_differ_on_the_sign() {
762        let cases = [(Opcode::Shl, -8_i128, 1_i128, -16_i128), (Opcode::AShr, -8, 1, -4)];
763        for (opcode, a, b, want) in cases {
764            let (_, mut func, block) = blank();
765            let mut build = Builder::new(&mut func, block);
766            let lhs = build.iconst(Type::int(64), a);
767            let rhs = build.iconst(Type::int(64), b);
768            let out = build.binary(opcode, lhs, rhs, Flags::NONE);
769            build.ret(&[out]);
770            assert!(fold(&mut func), "{opcode:?}");
771            assert_eq!(value_of(&func, out, Type::int(64)), Some(want), "{opcode:?}");
772        }
773        // The logical shift is the one that reads the value as bits, so minus eight shifted
774        // right by one is a very large positive number rather than minus four.
775        let (_, mut func, block) = blank();
776        let mut build = Builder::new(&mut func, block);
777        let lhs = build.iconst(Type::int(64), -8);
778        let rhs = build.iconst(Type::int(64), 1);
779        let out = build.binary(Opcode::LShr, lhs, rhs, Flags::NONE);
780        build.ret(&[out]);
781        assert!(fold(&mut func));
782        assert_eq!(value_of(&func, out, Type::int(64)), Some(i128::from(i64::MAX) - 3));
783    }
784
785    /// The one instruction under test, on one constant, folded as far as the pass takes it.
786    fn one(opcode: Opcode, ty: Type, arg: i128) -> Option<i128> {
787        let (_, mut func, block) = blank();
788        let mut build = Builder::new(&mut func, block);
789        let value = build.iconst(ty, arg);
790        let out = build.unary(opcode, value, ty);
791        build.ret(&[out]);
792        fold(&mut func);
793        value_of(&func, out, ty)
794    }
795
796    #[test]
797    fn the_bit_counts_are_evaluated_at_the_width_they_were_asked_at() {
798        let cases = [
799            (Opcode::Ctlz, 64, 0x0000_1000_0000_0000_i128, 19_i128),
800            (Opcode::Ctlz, 32, 0x0000_1000, 19),
801            (Opcode::Cttz, 64, 0x0000_1000_0000_0000, 44),
802            (Opcode::Cttz, 32, 0x0000_1000, 12),
803            (Opcode::Ctpop, 64, 0x0000_1000_0000_0000, 1),
804            (Opcode::Ctpop, 32, -1, 32),
805            (Opcode::Ctpop, 64, -1, 64),
806        ];
807        for (opcode, width, arg, want) in cases {
808            let ty = Type::int(width);
809            assert_eq!(one(opcode, ty, arg), Some(want), "{opcode:?} at {width} of {arg:#x}");
810        }
811    }
812
813    #[test]
814    fn a_search_for_a_bit_in_a_zero_answers_the_width_the_expansion_answers() {
815        for width in [8_u32, 16, 32, 64] {
816            let ty = Type::int(width);
817            let want = Some(i128::from(width));
818            assert_eq!(one(Opcode::Ctlz, ty, 0), want, "leading, at {width}");
819            assert_eq!(one(Opcode::Cttz, ty, 0), want, "trailing, at {width}");
820            assert_eq!(one(Opcode::Ctpop, ty, 0), Some(0), "count, at {width}");
821        }
822    }
823
824    #[test]
825    fn the_two_reversals_are_evaluated_and_a_byte_swap_of_a_part_of_a_byte_is_not() {
826        let ty = Type::int(32);
827        assert_eq!(one(Opcode::Bswap, ty, 0x1234_5678), Some(0x7856_3412));
828        assert_eq!(one(Opcode::Bswap, Type::int(16), 0x1234), Some(0x3412));
829        assert_eq!(one(Opcode::Bitreverse, Type::int(8), 0b1010_1100), Some(0b0011_0101));
830        // A width that is not a whole number of bytes has no byte swap, so there is nothing to
831        // evaluate and the instruction stays for the backend to refuse.
832        let (_, mut func, block) = blank();
833        let mut build = Builder::new(&mut func, block);
834        let value = build.iconst(Type::int(4), 0b1010);
835        let out = build.unary(Opcode::Bswap, value, Type::int(4));
836        build.ret(&[out]);
837        assert!(!fold(&mut func));
838    }
839
840    #[test]
841    fn a_comparison_of_two_constants_becomes_a_one_or_a_nought() {
842        let cases = [
843            (IntPred::Eq, 7_i128, 7_i128, true),
844            (IntPred::Eq, 7, 8, false),
845            (IntPred::Ne, 7, 8, true),
846            (IntPred::Slt, -1, 1, true),
847            (IntPred::Sle, -1, -1, true),
848            (IntPred::Sgt, -1, 1, false),
849            (IntPred::Sge, 1, -1, true),
850            // The same pair read as bits rather than as numbers, where minus one is the largest
851            // value there is and every unsigned answer is the opposite of the signed one.
852            (IntPred::Ult, -1, 1, false),
853            (IntPred::Ule, -1, 1, false),
854            (IntPred::Ugt, -1, 1, true),
855            (IntPred::Uge, -1, 1, true),
856        ];
857        for (pred, a, b, want) in cases {
858            let (_, mut func, block) = blank();
859            let mut build = Builder::new(&mut func, block);
860            let lhs = build.iconst(Type::int(64), a);
861            let rhs = build.iconst(Type::int(64), b);
862            let out = build.icmp(pred, lhs, rhs);
863            build.ret(&[out]);
864            assert!(fold(&mut func), "{pred:?} {a} {b}");
865            // The answer is one bit, where a set bit read as a signed number is minus one, so
866            // the question is which of the two constants it is rather than what it prints as.
867            let got = value_of(&func, out, Type::I1).expect("the comparison folded");
868            assert_eq!(got != 0, want, "{pred:?} {a} {b}");
869        }
870    }
871
872    #[test]
873    fn a_comparison_at_a_narrow_width_is_read_at_that_width() {
874        // Two hundred and fifty five stored in eight bits is minus one, so it is below one when
875        // the comparison is signed and above it when the comparison is not.
876        let ty = Type::int(8);
877        for (pred, want) in [(IntPred::Slt, true), (IntPred::Ult, false)] {
878            let (_, mut func, block) = blank();
879            let mut build = Builder::new(&mut func, block);
880            let lhs = build.iconst(ty, 255);
881            let rhs = build.iconst(ty, 1);
882            let out = build.icmp(pred, lhs, rhs);
883            build.ret(&[out]);
884            assert!(fold(&mut func), "{pred:?}");
885            let got = value_of(&func, out, Type::I1).expect("the comparison folded");
886            assert_eq!(got != 0, want, "{pred:?}");
887        }
888    }
889
890    #[test]
891    fn a_comparison_with_one_constant_operand_is_left_alone() {
892        let (_, mut func, block) = blank();
893        let ty = Type::int(64);
894        let param = func.append_param(block, ty);
895        let mut build = Builder::new(&mut func, block);
896        let rhs = build.iconst(ty, 3);
897        let out = build.icmp(IntPred::Eq, param, rhs);
898        build.ret(&[out]);
899        assert!(!fold(&mut func));
900    }
901
902    #[test]
903    fn a_bit_count_of_something_that_is_not_a_constant_is_left_alone() {
904        for opcode in [Opcode::Ctlz, Opcode::Cttz, Opcode::Ctpop, Opcode::Bswap] {
905            let (_, mut func, block) = blank();
906            let ty = Type::int(64);
907            let param = func.append_param(block, ty);
908            let mut build = Builder::new(&mut func, block);
909            let out = build.unary(opcode, param, ty);
910            build.ret(&[out]);
911            assert!(!fold(&mut func), "{opcode:?}");
912        }
913    }
914
915    #[test]
916    fn a_shift_by_the_width_or_more_is_left_alone_because_the_language_does_not_define_it() {
917        for count in [64_i128, 65, -1] {
918            let (_, mut func, block) = blank();
919            let mut build = Builder::new(&mut func, block);
920            let lhs = build.iconst(Type::int(64), 1);
921            let rhs = build.iconst(Type::int(64), count);
922            let out = build.binary(Opcode::Shl, lhs, rhs, Flags::NONE);
923            build.ret(&[out]);
924            assert!(!fold(&mut func), "a shift by {count} was folded");
925        }
926    }
927
928    #[test]
929    fn an_operation_that_wraps_folds_and_the_same_one_promising_it_will_not_does_not() {
930        let big = i128::from(i32::MAX);
931        for (flags, folds) in [(Flags::NONE, true), (Flags::NSW, false)] {
932            let (_, mut func, block) = blank();
933            let mut build = Builder::new(&mut func, block);
934            let lhs = build.iconst(Type::int(32), big);
935            let rhs = build.iconst(Type::int(32), 1);
936            let out = build.binary(Opcode::Add, lhs, rhs, flags);
937            build.ret(&[out]);
938            assert_eq!(fold(&mut func), folds, "{flags}");
939            if folds {
940                assert_eq!(value_of(&func, out, Type::int(32)), Some(i128::from(i32::MIN)));
941            }
942        }
943    }
944
945    #[test]
946    fn an_unsigned_promise_is_broken_by_a_negative_result_as_well_as_by_a_large_one() {
947        let (_, mut func, block) = blank();
948        let mut build = Builder::new(&mut func, block);
949        let lhs = build.iconst(Type::int(32), 1);
950        let rhs = build.iconst(Type::int(32), 2);
951        let out = build.binary(Opcode::Sub, lhs, rhs, Flags::NUW);
952        build.ret(&[out]);
953        assert!(!fold(&mut func));
954    }
955
956    #[test]
957    fn an_operation_with_one_constant_operand_is_left_alone() {
958        let (_, mut func, block) = blank();
959        let param = func.append_param(block, Type::int(64));
960        let mut build = Builder::new(&mut func, block);
961        let rhs = build.iconst(Type::int(64), 7);
962        let out = build.binary(Opcode::Add, param, rhs, Flags::NONE);
963        build.ret(&[out]);
964        assert!(!fold(&mut func));
965        assert_eq!(func[out_inst(&func, out)].opcode, Opcode::Add);
966    }
967
968    #[test]
969    fn a_conversion_to_an_integer_truncates_toward_zero() {
970        for (text, expected) in [("2.75", 2_i128), ("-2.75", -2), ("0.5", 0), ("-0.5", 0)] {
971            let (_, mut func, block) = blank();
972            let mut build = Builder::new(&mut func, block);
973            let value = number(&mut build, text, Type::float(Float::F64));
974            let out = build.unary(Opcode::FPToSI, value, Type::int(32));
975            build.ret(&[out]);
976            assert!(fold(&mut func), "{text}");
977            assert_eq!(value_of(&func, out, Type::int(32)), Some(expected), "{text}");
978        }
979    }
980
981    #[test]
982    fn a_negative_number_converts_to_an_unsigned_type_only_when_truncating_lands_on_zero() {
983        for (text, expected) in [("-0.5", Some(0)), ("-1.5", None)] {
984            let (_, mut func, block) = blank();
985            let mut build = Builder::new(&mut func, block);
986            let value = number(&mut build, text, Type::float(Float::F64));
987            let out = build.unary(Opcode::FPToUI, value, Type::int(32));
988            build.ret(&[out]);
989            assert_eq!(fold(&mut func), expected.is_some(), "{text}");
990            assert_eq!(value_of(&func, out, Type::int(32)), expected, "{text}");
991        }
992    }
993
994    #[test]
995    fn a_number_the_destination_type_has_no_room_for_is_left_alone() {
996        let (_, mut func, block) = blank();
997        let mut build = Builder::new(&mut func, block);
998        let value = number(&mut build, "1e30", Type::float(Float::F64));
999        let out = build.unary(Opcode::FPToSI, value, Type::int(32));
1000        build.ret(&[out]);
1001        assert!(!fold(&mut func));
1002        assert_eq!(func[out_inst(&func, out)].opcode, Opcode::FPToSI);
1003    }
1004
1005    #[test]
1006    fn a_nan_is_left_alone() {
1007        let (_, mut func, block) = blank();
1008        let mut build = Builder::new(&mut func, block);
1009        let value = build.fconst(Type::float(Float::F64), 0x7ff8_0000_0000_0000);
1010        let out = build.unary(Opcode::FPToSI, value, Type::int(32));
1011        build.ret(&[out]);
1012        assert!(!fold(&mut func));
1013    }
1014
1015    #[test]
1016    fn a_constant_is_read_in_the_format_its_own_type_gives_it() {
1017        // The same hundred and twenty eight bits, which are an x87 three and an `f128` far too
1018        // small to be anything but zero once it has been truncated.
1019        let bits = super::Float::parse("3.0", Format::X87Extended).expect("a number").0.to_bits();
1020        for (float, expected) in [(Float::F80, 3_i128), (Float::F128, 0)] {
1021            let (_, mut func, block) = blank();
1022            let mut build = Builder::new(&mut func, block);
1023            let value = build.fconst(Type::float(float), bits);
1024            let out = build.unary(Opcode::FPToSI, value, Type::int(32));
1025            build.ret(&[out]);
1026            assert!(fold(&mut func), "{float}");
1027            assert_eq!(value_of(&func, out, Type::int(32)), Some(expected), "{float}");
1028        }
1029    }
1030
1031    #[test]
1032    fn a_conversion_of_something_that_is_not_a_constant_is_left_alone() {
1033        let (_, mut func, block) = blank();
1034        let param = func.append_param(block, Type::float(Float::F64));
1035        let mut build = Builder::new(&mut func, block);
1036        let out = build.unary(Opcode::FPToSI, param, Type::int(32));
1037        build.ret(&[out]);
1038        assert!(!fold(&mut func));
1039    }
1040
1041    #[test]
1042    fn a_divide_is_not_folded_even_when_both_operands_are_constants() {
1043        for opcode in [Opcode::SDiv, Opcode::UDiv, Opcode::SRem, Opcode::URem] {
1044            let (_, mut func, block) = blank();
1045            let mut build = Builder::new(&mut func, block);
1046            let lhs = build.iconst(Type::int(64), 42);
1047            let rhs = build.iconst(Type::int(64), 7);
1048            let out = build.binary(opcode, lhs, rhs, Flags::NONE);
1049            build.ret(&[out]);
1050            assert!(!fold(&mut func), "{opcode:?}");
1051        }
1052    }
1053
1054    #[test]
1055    fn folding_leaves_the_function_something_the_verifier_accepts() {
1056        let mut names = Interner::new();
1057        let name = names.intern("f");
1058        let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(64)]));
1059        let block = func.create_block();
1060        let mut build = Builder::new(&mut func, block);
1061        let narrow = build.iconst(Type::int(32), 7);
1062        let wide = build.unary(Opcode::SExt, narrow, Type::int(64));
1063        build.ret(&[wide]);
1064        assert!(fold(&mut func));
1065        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1066        let module_name = names.intern("m");
1067        let mut module = Module::new(module_name, &target);
1068        module.add_func(func);
1069        rucc_ir::verify(&module, &names).expect("folding does not break the IR");
1070    }
1071
1072    #[test]
1073    fn fuel_stops_the_transformation_and_not_the_walk() {
1074        let build_two = |func: &mut Func, block: Block| {
1075            let mut build = Builder::new(func, block);
1076            let a = build.iconst(Type::int(32), 7);
1077            let wide_a = build.unary(Opcode::SExt, a, Type::int(64));
1078            let b = build.iconst(Type::int(32), 9);
1079            let wide_b = build.unary(Opcode::SExt, b, Type::int(64));
1080            let sum = build.binary(Opcode::Add, wide_a, wide_b, Flags::NONE);
1081            build.ret(&[sum]);
1082            (wide_a, wide_b)
1083        };
1084
1085        let (_, mut none, block) = blank();
1086        let (first, _) = build_two(&mut none, block);
1087        let stats =
1088            Fold.run(&mut none, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
1089        assert!(!stats.changed());
1090        assert_eq!(none[out_inst(&none, first)].opcode, Opcode::SExt);
1091        // Both of them looked at and neither of them folded, which is the count a bisection is
1092        // reading: how many sites are left past where the fuel ran out.
1093        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 2);
1094
1095        let (_, mut one, block) = blank();
1096        let (first, second) = build_two(&mut one, block);
1097        let mut fuel = Fuel::of(1);
1098        let stats = Fold.run(&mut one, &mut crate::machine::fixtures::analyses(), &mut fuel);
1099        assert!(stats.changed());
1100        assert_eq!(fuel.spent(), 1);
1101        assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1102        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1103        assert_eq!(one[out_inst(&one, first)].opcode, Opcode::IConst);
1104        assert_eq!(one[out_inst(&one, second)].opcode, Opcode::SExt);
1105    }
1106
1107    #[test]
1108    fn folding_one_operation_uncovers_the_next() {
1109        let (_, mut func, block) = blank();
1110        let mut build = Builder::new(&mut func, block);
1111        let a = build.iconst(Type::int(32), 7);
1112        let wide = build.unary(Opcode::SExt, a, Type::int(64));
1113        let b = build.iconst(Type::int(64), 9);
1114        let sum = build.binary(Opcode::Add, wide, b, Flags::NONE);
1115        build.ret(&[sum]);
1116        assert!(fold(&mut func));
1117        // One walk in order is enough for this shape, because a constant is written before it
1118        // is used and the walk is in the same order.
1119        assert_eq!(value_of(&func, sum, Type::int(64)), Some(16));
1120    }
1121
1122    #[test]
1123    fn a_constant_is_left_where_it_is_and_folding_it_again_changes_nothing() {
1124        let (_, mut func, block) = blank();
1125        let mut build = Builder::new(&mut func, block);
1126        let a = build.iconst(Type::int(32), 7);
1127        let wide = build.unary(Opcode::SExt, a, Type::int(64));
1128        build.ret(&[wide]);
1129        assert!(fold(&mut func));
1130        assert!(!fold(&mut func), "a second run found something to do");
1131    }
1132
1133    /// The instruction that defines a value, which every value in these tests has.
1134    fn out_inst(func: &Func, value: Value) -> rucc_ir::Inst {
1135        match func[value].def {
1136            rucc_ir::Def::Result { inst, .. } => inst,
1137            rucc_ir::Def::Param { .. } => panic!("a parameter has no instruction"),
1138        }
1139    }
1140}