Skip to main content

rucc_opt/
libcall.rs

1//! A call to the library, folded into the call it really is.
2//!
3//! Section 20.2 of `spec/optimizer/20-idioms-and-libcalls.md`, the half of it that is about a
4//! library call rather than about arithmetic. `printf("hello world\n")` writes the same bytes as
5//! `puts("hello world")`, and the second one does not read a format string at run time, so gcc
6//! rewrites it and has done since 2000. A program that checks which of the two it was left with,
7//! which is what `gcc.c-torture/execute/builtins/printf.c` does by defining a `printf` of its own
8//! that aborts, fails outright on a compiler that leaves the call alone. tamnd/rucc#1636 is that
9//! program and the two beside it.
10//!
11//! # The rules
12//!
13//! All of them measured against gcc 16.2.0 on x86-64 rather than read out of its source, and all of
14//! them conditional on the format being a string this module holds the bytes of.
15//!
16//! `strstr(s, "")` is `s`, since the empty string is found at once wherever it is looked for.
17//! `strstr(s, "w")` is `strchr(s, 'w')`, which is a search for a character rather than for a string
18//! and is worth doing wherever the haystack came from. `strstr` of two strings this module holds is
19//! the answer itself, which is a place in the haystack or a null pointer, and nothing is called.
20//!
21//! `strlen`, `strnlen`, `strcmp`, `strncmp`, `strchr`, `strrchr`, `memchr`, `strspn`, `strcspn` and
22//! `strpbrk` of strings this module holds are the answer itself in the same way, which is a number
23//! for the first four and the last two of the six that search, and a place in the first argument or
24//! a null pointer for the other ones. The number goes in with the width the call was declared to
25//! give back, because a program that declared `strlen` as something returning an `int` is a program
26//! whose reader of that answer reads an `int`.
27//!
28//! Three of them have a rule about the shape rather than about the bytes. `strpbrk(s, "")` is a
29//! null pointer and `strspn(s, "")` is zero, since nothing at all is in an empty set, and
30//! `strcspn(s, "")` is `strlen(s)` for the same reason read the other way. `strpbrk(s, "c")` is
31//! `strchr(s, 'c')`, which is the `strstr` rule again for a set of one character instead of a
32//! needle of one.
33//!
34//! Two of them are told how far to read rather than going looking for a terminator, and those two
35//! read the object's bytes rather than the string in it. `memchr(s, c, n)` needs the object to have
36//! `n` bytes from `s` on, and it is refused where it does not, since a call reading past the end of
37//! what the compiler can see is a call whose answer the compiler does not know. `strnlen(s, n)` is
38//! the count where nothing terminated the string inside it.
39//!
40//! `printf` with the format alone: nothing at all when it is empty, `putchar` when it is one
41//! character, and `puts` of the format without its last character when the format holds no `%` and
42//! ends in a newline. `printf("%s\n", p)` is `puts(p)` and `printf("%c", c)` is `putchar(c)`,
43//! whatever `p` and `c` are. `printf("%s", p)` where `p` is a string this module holds is the same
44//! question again asked of that string, and where it is not, the call stays: `printf` has no stream
45//! argument to hand to `fputs`, and `stdout` is not a name a compiler may invent.
46//!
47//! `fprintf` is the same list with a stream in hand, so the case `printf` cannot take is the case
48//! this one can. A format holding no `%` becomes `fputc` of its one character or `fwrite` of the
49//! whole of it, `fprintf(s, "%c", c)` becomes `fputc(c, s)`, and `fprintf(s, "%s", p)` becomes
50//! `fputs(p, s)` however little is known about `p`.
51//!
52//! `fputs(p, s)` needs the length of `p` and nothing else. Zero is nothing at all, one is `fputc`
53//! when the character is known as well, and anything longer is `fwrite(p, 1, len, s)`.
54//!
55//! The `_unlocked` spellings get the one fold that names no function, which is that a call writing
56//! nothing is removed. gcc stops in exactly the same place and the reason is in the torture
57//! program's own comment: a system need not have a `puts_unlocked` for the compiler to name.
58//!
59//! The checking spellings `_FORTIFY_SOURCE` writes, `__memcpy_chk` and the thirteen beside it,
60//! carry the size of the destination as one more argument and abort when the call would not fit.
61//! Where that size is all ones, which is `__builtin_object_size` not knowing, or where what the
62//! call writes is known to fit, the check cannot fail and the call is the plain one without it.
63//! Where it can fail the call may still get cheaper: a checking `stpcpy` whose answer nothing reads
64//! is a checking `strcpy`, and a checking `strcpy` of a known string is a checking `memcpy`. An
65//! append of nothing is the destination. The plain name has to be one the module does not declare
66//! with some other shape, and a call made plain is looked at again, up to three times.
67//!
68//! # What a call has to be
69//!
70//! For the printf family, its result has to be read by nothing. `printf` answers the number of
71//! characters written and `puts` answers a non-negative number that is not that count, so a program
72//! looking at the answer is a program this may not touch. The str and mem families are the other
73//! way round: the answer is the whole point of the call and the fold produces it, so a program
74//! reading it is the ordinary case.
75//!
76//! It has to give back one value of the kind its name says it does. A program that declared
77//! `strchr` as something returning two values, or a number, declared a function of its own and a
78//! pointer into a string literal is not what it answers.
79//!
80//! The name has to be the one the source spelled rather than the one the object file will carry.
81//! `extern char *strstr (const char *, const char *) __asm ("my_strstr");` is a declaration of
82//! `strstr`, and a compiler that reads the symbol alone sees a call to a function it knows nothing
83//! about. So the callee is looked up through [`rucc_ir::Func::spelled`], and a call this leaves
84//! behind is a call to whatever symbol the module says that name has, which is the rename again
85//! read from the other end.
86//!
87//! The name has to be one this module does not define. A translation unit holding the body of its
88//! own `fputs` means that body, which is the rule [`crate::heap`] applies to `malloc` and for the
89//! same reason.
90//!
91//! The function must not carry memory SSA yet, which where this runs it does not. Memory is
92//! threaded by [`crate::number`], that pass is in the function pipeline, and this runs before the
93//! pipeline starts. The check is here anyway, because a call with a memory operand rewritten into
94//! one without would be a use of a value nothing defines.
95//!
96//! # Where it runs, and why it is not a rule
97//!
98//! Section 20.2 asks for folds like these to be rules in the rewrite DSL with the callee's identity
99//! in the pattern, and most of them can be. These cannot. A rule rewrites one instruction into
100//! instructions, and two of the rewrites here need something no rule has: the name `puts` has to be
101//! interned before a call can name it, and `printf("hello world\n")` has to leave behind a string
102//! that is not in the module yet, because "hello world" with a terminator is not a suffix of
103//! "hello world\n" with one. So this is a module at a time transformation beside [`crate::ipcp`]
104//! and [`crate::ipasra`], which is where the interner and the module both are.
105//!
106//! `-O1` and above, which is one level below where those two run. gcc folds these at `-O1`, the
107//! torture programs are compiled at every level from `-O1` up, and the fold makes the program
108//! smaller as well as faster, so there is no level above `-O0` where declining it is right.
109//!
110//! Off under `-fno-builtin` and `-ffreestanding`, which is the flag pair section 20.1 describes,
111//! and off for one name at a time under `-fno-builtin-<name>`. A freestanding program left with a
112//! call to a `puts` it never wrote is a link failure, and that is the whole reason the flag exists.
113
114use std::collections::{HashMap, HashSet};
115
116use rucc_base::{Interner, Symbol};
117use rucc_ir::{
118    AbiList, AttrSet, Block, CallInfo, Datum, Def, Extra, Float, Func, FuncId, Global, Imm, Inst,
119    InstData, IntPred, Linkage, MemInfo, MemOrder, Module, Opcode, Pic, Restrict, Signature,
120    SymbolRef, Type, Value,
121};
122
123use crate::extents::vouched;
124use crate::{Cfg, Fuel, Stats, uses};
125
126/// What the pass is called in `-fopt-info` and `-fpass-fuel=`.
127pub const NAME: &str = "libcall";
128
129/// How many block parameters deep the walk that answers "what string is this" goes.
130///
131/// A conditional expression whose arms are two literals is one level, which is what
132/// `builtins/fputs.c` writes twice. Four is room for that nested three deep and is the bound that
133/// stops a walk which would otherwise go round a loop forever. The same number bounds the walk
134/// down a chain of `ptr_add`, where one level is one index written in the source.
135const DEPTH: u32 = 4;
136
137/// How long a chain of block parameters the walk for a string follows.
138///
139/// Longer than [`DEPTH`], because an `if` and `else if` chain that picks a string in a loop is one
140/// block parameter per arm joining the next, and `builtins/stpcpy-chk.c` has four arms inside the
141/// loop on top of the loop's own parameter. A parameter already on the walk is not walked again,
142/// so this bounds a chain rather than a loop.
143const CHAIN: u32 = 12;
144
145/// How many times the calls in one function are looked at, which is the longest chain of folds
146/// where each one leaves a call behind that the next one folds. `builtins/strcat.c` nests six
147/// `strcat` calls, and what the writes in front of one say is only known once the one inside it is
148/// a copy, so that is a round each. Rounds stop as soon as one finds nothing to do.
149const ROUNDS: u32 = 8;
150
151/// The names a fold may leave behind, sorted.
152const REPLACEMENTS: [&str; 16] = [
153    "__memcpy_chk",
154    "ceilf",
155    "floorf",
156    "fputc",
157    "fputs",
158    "fwrite",
159    "memcpy",
160    "nearbyintf",
161    "putchar",
162    "puts",
163    "rintf",
164    "roundf",
165    "strchr",
166    "strcpy",
167    "strlen",
168    "truncf",
169];
170
171/// The names a checking call may become once its check cannot fail, sorted.
172///
173/// These are not in [`REPLACEMENTS`] because what a call to one of them looks like is read off the
174/// call it replaces rather than written down here. Half of them are variadic or take a `va_list`,
175/// and what a `va_list` is travels with the target, so the one signature that is right is the one
176/// the checking call already had with the checking arguments taken out of it.
177const UNCHECKED: [&str; 18] = [
178    "__memcpy_chk",
179    "__strcat_chk",
180    "__strcpy_chk",
181    "__strncpy_chk",
182    "memcpy",
183    "memmove",
184    "mempcpy",
185    "memset",
186    "snprintf",
187    "sprintf",
188    "stpcpy",
189    "stpncpy",
190    "strcat",
191    "strcpy",
192    "strncat",
193    "strncpy",
194    "vsnprintf",
195    "vsprintf",
196];
197
198/// The names a fold reads, sorted.
199const SOURCES: [&str; 55] = [
200    "__fprintf_chk",
201    "__memcpy_chk",
202    "__memmove_chk",
203    "__mempcpy_chk",
204    "__memset_chk",
205    "__printf_chk",
206    "__snprintf_chk",
207    "__sprintf_chk",
208    "__stpcpy_chk",
209    "__stpncpy_chk",
210    "__strcat_chk",
211    "__strcpy_chk",
212    "__strncat_chk",
213    "__strncpy_chk",
214    "__vfprintf_chk",
215    "__vprintf_chk",
216    "__vsnprintf_chk",
217    "__vsprintf_chk",
218    "bcopy",
219    "ceil",
220    "floor",
221    "fprintf",
222    "fprintf_unlocked",
223    "fputs",
224    "fputs_unlocked",
225    "index",
226    "memchr",
227    "memcmp",
228    "memmove",
229    "mempcpy",
230    "nearbyint",
231    "printf",
232    "printf_unlocked",
233    "rindex",
234    "rint",
235    "round",
236    "sprintf",
237    "stpcpy",
238    "strcat",
239    "strchr",
240    "strcmp",
241    "strcpy",
242    "strcspn",
243    "strlen",
244    "strncat",
245    "strncmp",
246    "strncpy",
247    "strnlen",
248    "strpbrk",
249    "strrchr",
250    "strspn",
251    "strstr",
252    "trunc",
253    "vfprintf",
254    "vprintf",
255];
256
257/// What the compiler worked out a call writes, which is what it is replaced by.
258#[derive(Debug, Clone, PartialEq, Eq)]
259enum Plan {
260    /// It writes nothing, so it goes and nothing takes its place.
261    Drop,
262    /// The answer is a place in an argument the call was given, or nowhere at all, and that answer
263    /// takes the place of the call's result.
264    Answer(Answer),
265    /// This call takes its place.
266    Swap {
267        /// The symbol the replacement names, which is what the module calls that function.
268        callee: Symbol,
269        /// What that function takes and returns.
270        signature: Signature,
271        /// What to pass it.
272        args: Vec<Argument>,
273        /// What takes the place of the old call's result, where that is not the new call's
274        /// result. `stpcpy` of a string whose length is known is a `memcpy` whose answer is the
275        /// start of the copy, and the answer `stpcpy` gives is the end of it.
276        answer: Option<Answer>,
277    },
278    /// The same call to another function, with the arguments at these places left out.
279    ///
280    /// This is what a call to one of the checking functions `_FORTIFY_SOURCE` writes becomes once
281    /// its check cannot fail. What the new call looks like is the old one without those
282    /// arguments, including whatever the old one passed beyond its named parameters.
283    Unchecked {
284        /// The symbol the new call names.
285        callee: Symbol,
286        /// The places of the arguments that go.
287        drop: &'static [usize],
288    },
289    /// The same rounding done in `float` on the `float` the argument was widened from, with the
290    /// answer widened after it.
291    ///
292    /// Every `float` is a `double` exactly, and rounding one to a whole number gives a whole number
293    /// a `float` holds, so `floor ((double) f)` and `(double) floorf (f)` are the same number.
294    Narrow {
295        /// The symbol the `float` spelling carries.
296        callee: Symbol,
297        /// What that function takes and returns.
298        signature: Signature,
299        /// The `float` the argument was widened from.
300        arg: Value,
301    },
302}
303
304impl Plan {
305    /// Names what each value was renamed to wherever this plan names the old one.
306    fn rename(&mut self, renamed: &HashMap<Value, Value>) {
307        if renamed.is_empty() {
308            return;
309        }
310        let answer = match self {
311            Plan::Drop | Plan::Unchecked { .. } => None,
312            Plan::Narrow { arg, .. } => {
313                *arg = renamed.get(arg).copied().unwrap_or(*arg);
314                None
315            }
316            Plan::Answer(answer) => Some(answer),
317            Plan::Swap { args, answer, .. } => {
318                for arg in args {
319                    if let Argument::Have(value) | Argument::At(value, _) = arg {
320                        *value = renamed.get(value).copied().unwrap_or(*value);
321                    }
322                }
323                answer.as_mut()
324            }
325        };
326        let value = match answer {
327            Some(Answer::Along(value, _) | Answer::Least { count: value, .. }) => value,
328            Some(Answer::Byte { of, .. } | Answer::Less { step: of, .. }) => of,
329            Some(Answer::Nowhere | Answer::Number(_)) | None => return,
330        };
331        *value = renamed.get(value).copied().unwrap_or(*value);
332    }
333}
334
335/// What a call that answers rather than writes was going to answer.
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337enum Answer {
338    /// That many bytes along from a value the call was handed.
339    Along(Value, u64),
340    /// Nowhere in it, which is a null pointer.
341    Nowhere,
342    /// That number, in whatever type the call was declared to give back.
343    ///
344    /// The type is read off the call rather than worked out from the name, because a program that
345    /// declared `strlen` as something returning an `int` gets an `int`, and a constant of the
346    /// width the call already had is the only one that can take its place.
347    Number(i128),
348    /// The smaller of a count the call was given and a length the compiler knows, compared as
349    /// unsigned numbers, which is what `strnlen` answers over a string the module holds.
350    Least {
351        /// The count, which nothing is known about.
352        count: Value,
353        /// The length of the string, up to its terminator.
354        len: u64,
355    },
356    /// A length the compiler knows less a step nothing is known about but how large it can be,
357    /// which is what `strlen` of a string the module holds answers at a place inside it that the
358    /// program worked out.
359    Less {
360        /// The length of the string from where the step is taken, up to its terminator.
361        len: u64,
362        /// How far along the string the call was handed a pointer to, in bytes.
363        step: Value,
364    },
365    /// One byte of a string the call was given against a byte the compiler knows.
366    ///
367    /// This is the one answer that is an instruction rather than a constant or an address, because
368    /// the byte is in memory and has to be read. Both bytes are `unsigned char` values, which is
369    /// what the standard says a comparison compares, and the answer is the difference between them
370    /// in whichever order the call wrote its arguments.
371    Byte {
372        /// The string nothing is known about, whose first byte is read.
373        of: Value,
374        /// The first byte of the string the module holds, or zero where that string is empty.
375        against: u8,
376        /// Whether the string the module holds was the call's first argument, which is what says
377        /// which way round the difference goes.
378        leading: bool,
379    },
380}
381
382/// Which of the places a character appears in a string a search wants.
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384enum Side {
385    /// `strchr`.
386    First,
387    /// `strrchr`.
388    Last,
389}
390
391/// Which way round the test in a span is.
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393enum Set {
394    /// `strspn`, which walks while the character is one of the set.
395    Inside,
396    /// `strcspn`, which walks while it is not.
397    Outside,
398}
399
400/// One argument of a replacement call.
401#[derive(Debug, Clone, PartialEq, Eq)]
402enum Argument {
403    /// A value the call being replaced already had.
404    Have(Value),
405    /// An `int` constant, which in every case here is a character.
406    Char(u8),
407    /// A `size_t` constant, which in every case here is a count of bytes.
408    Count(u64),
409    /// The address of a read only object holding these bytes and a terminator.
410    Text(Vec<u8>),
411    /// A value the call being replaced already had, that many bytes further on.
412    At(Value, u64),
413}
414
415/// What this module already says about each name a fold may leave behind.
416///
417/// The verifier holds that a call to a name the module declares carries that name's own signature,
418/// so a program that declared `fwrite` through `<stdio.h>` decides what a call to it looks like and
419/// a program that declared it as something else stops the fold. The alternative is a fold that
420/// produces IR the verifier refuses, which is a compiler that crashes on a program gcc compiles.
421struct Shapes {
422    /// The symbol a call to that name has to carry and the signature it has to have, and `None`
423    /// where no call may name it.
424    held: HashMap<&'static str, Option<(Symbol, Signature)>>,
425    /// The same for each name in [`UNCHECKED`], where the signature is the one the module declared
426    /// and `None` where it declared nothing, since the call a checking call becomes brings its own.
427    named: HashMap<&'static str, Option<(Symbol, Option<Signature>)>>,
428}
429
430impl Shapes {
431    /// Reads the module's answer for each of the names a fold may leave behind.
432    fn of(module: &Module, names: &mut Interner) -> Self {
433        let mut held: HashMap<&'static str, Option<(Symbol, Signature)>> = REPLACEMENTS
434            .iter()
435            .map(|&name| (name, Some((names.intern(name), canonical(module, name)))))
436            .collect();
437        let mut named: HashMap<&'static str, Option<(Symbol, Option<Signature>)>> =
438            UNCHECKED.iter().map(|&name| (name, Some((names.intern(name), None)))).collect();
439        for id in module.funcs() {
440            // The name the source gave it, so that a module which renamed `puts` is left with a
441            // call to the symbol it renamed it to rather than one to a `puts` it never declared.
442            let func = &module[id];
443            let spelled = func.spelled.unwrap_or(func.name);
444            if let Some(slot) = named.get_mut(names.resolve(spelled)) {
445                *slot = Some((func.name, Some(func.signature().clone())));
446            }
447            let Some(slot) = held.get_mut(names.resolve(spelled)) else { continue };
448            let declared = func.signature();
449            let agrees = slot.as_ref().is_some_and(|(_, want)| {
450                !declared.variadic
451                    && declared.param_types().eq(want.param_types())
452                    && declared.return_types().eq(want.return_types())
453            });
454            *slot = agrees.then(|| (func.name, declared.clone()));
455        }
456        // A variable or a second name for something else is not a function to call, whatever it is
457        // spelled.
458        for id in module.globals() {
459            let name = names.resolve(module[id].name);
460            if let Some(slot) = held.get_mut(name) {
461                *slot = None;
462            }
463            if let Some(slot) = named.get_mut(name) {
464                *slot = None;
465            }
466        }
467        for id in module.aliases() {
468            let name = names.resolve(module[id].name);
469            if let Some(slot) = held.get_mut(name) {
470                *slot = None;
471            }
472            if let Some(slot) = named.get_mut(name) {
473                *slot = None;
474            }
475        }
476        Self { held, named }
477    }
478
479    /// What a call to that name carries, or `None` where this module does not allow one.
480    fn get(&self, name: &'static str) -> Option<(Symbol, Signature)> {
481        self.held.get(name)?.clone()
482    }
483
484    /// What a call to that name carries, where a call to it with this signature is one the module
485    /// allows.
486    fn unchecked(&self, name: &str, want: &Signature) -> Option<Symbol> {
487        let (symbol, declared) = self.named.get(name)?.as_ref()?;
488        let agrees = declared.as_ref().is_none_or(|declared| {
489            declared.variadic == want.variadic
490                && declared.param_types().eq(want.param_types())
491                && declared.return_types().eq(want.return_types())
492        });
493        agrees.then_some(*symbol)
494    }
495}
496
497/// The signature a call to that name carries where nothing in the module declared it.
498///
499/// What the frontend already writes, which is how `__builtin_putchar` reaches `putchar` in a
500/// program that never named it.
501fn canonical(module: &Module, name: &str) -> Signature {
502    let int = int();
503    let size = size(module);
504    match name {
505        "puts" => Signature::new().with_params(&[Type::PTR]).with_returns(&[int]),
506        "putchar" => Signature::new().with_params(&[int]).with_returns(&[int]),
507        "fputc" => Signature::new().with_params(&[int, Type::PTR]).with_returns(&[int]),
508        "fputs" => Signature::new().with_params(&[Type::PTR, Type::PTR]).with_returns(&[int]),
509        "strchr" => Signature::new().with_params(&[Type::PTR, int]).with_returns(&[Type::PTR]),
510        "strlen" => Signature::new().with_params(&[Type::PTR]).with_returns(&[size]),
511        "strcpy" => {
512            Signature::new().with_params(&[Type::PTR, Type::PTR]).with_returns(&[Type::PTR])
513        }
514        "memcpy" => {
515            Signature::new().with_params(&[Type::PTR, Type::PTR, size]).with_returns(&[Type::PTR])
516        }
517        "ceilf" | "floorf" | "nearbyintf" | "rintf" | "roundf" | "truncf" => {
518            let float = Type::float(Float::F32);
519            Signature::new().with_params(&[float]).with_returns(&[float])
520        }
521        "__memcpy_chk" => Signature::new()
522            .with_params(&[Type::PTR, Type::PTR, size, size])
523            .with_returns(&[Type::PTR]),
524        // `fwrite`, the one that is told how many bytes to write rather than going looking for a
525        // terminator, and the only one of the five whose types are the target's rather than fixed.
526        _ => {
527            Signature::new().with_params(&[Type::PTR, size, size, Type::PTR]).with_returns(&[size])
528        }
529    }
530}
531
532/// The type an `int` is in the IR.
533///
534/// Thirty two bits on every target this compiler has a back end for, which is why it is written
535/// here rather than asked of the layout. The day one of them says otherwise, a fold under this rule
536/// would hand `putchar` the wrong width, and this is the one place that would have to change.
537const fn int() -> Type {
538    Type::int(32)
539}
540
541/// The type a `size_t` is on the target this module is for.
542fn size(module: &Module) -> Type {
543    Type::int(module.datalayout.pointer_bits)
544}
545
546/// Folds every call in the module whose output the compiler can work out.
547///
548/// Gives back the functions that changed and what changed in them, which is what the pipeline turns
549/// into `-fopt-info` remarks.
550pub fn fold(
551    module: &mut Module,
552    names: &mut Interner,
553    no_builtin: &[String],
554    pic: Pic,
555    fuel: &mut Fuel,
556) -> Vec<(FuncId, Stats)> {
557    let shapes = Shapes::of(module, names);
558    // What each symbol was called in the source, for the declarations where the two differ. A call
559    // names a symbol, and a symbol an assembler name replaced says nothing about which library
560    // function it is, so this is what the two names are put back together through.
561    let standard: HashMap<Symbol, Symbol> =
562        module.funcs().filter_map(|id| Some((module[id].name, module[id].spelled?))).collect();
563    // A body the program wrote for one of these names does not stop a call to that name being
564    // folded, which is gcc 16's rule as well: only `-fno-builtin` says a standard name is not the
565    // standard function. What it does stop is folding inside that body, since a `puts` of the
566    // program's own with a `printf` of a newline in it would otherwise be a call to itself.
567    let library: HashSet<Symbol> = module
568        .funcs()
569        .filter(|&id| !module[id].is_declaration())
570        .map(|id| module[id].name)
571        .filter(|&name| {
572            let name = names.resolve(standard.get(&name).copied().unwrap_or(name));
573            SOURCES.contains(&name) || REPLACEMENTS.contains(&name)
574        })
575        .collect();
576    // One table for the module rather than one per function, so that two calls folded to the same
577    // string share one object instead of each getting one of its own.
578    let mut texts: HashMap<Vec<u8>, Symbol> = HashMap::new();
579    let mut done = Vec::new();
580    for id in module.funcs().collect::<Vec<FuncId>>() {
581        // A function with none of these names in it is most of them, and the answer for one is a
582        // walk over its instructions that allocates nothing. The two tables below are a vector and
583        // a predecessor list per block, which is a cost worth not paying over a module whose
584        // functions print nothing.
585        if module[id].is_declaration()
586            || library.contains(&module[id].name)
587            || !mentions(&module[id], names, &standard)
588        {
589            continue;
590        }
591        let mut stats = Stats::new();
592        // A fold can leave behind a call another fold knows, which is how `__stpcpy_chk` of a
593        // string that fits becomes `stpcpy` and then a `memcpy` with the end of the copy as its
594        // answer. Each round is one step along a chain like that, and `strcat` onto `strcat` is another.
595        for _ in 0..ROUNDS {
596            // The whole body is read before any of it changes. A plan names values the body holds,
597            // and working the next one out from a body half rewritten is how a pass comes to read
598            // a value whose definition it has just taken away.
599            let plans = {
600                let func = &module[id];
601                let site = Site {
602                    module,
603                    func,
604                    cfg: &Cfg::new(func),
605                    shapes: &shapes,
606                    counts: &uses::count(func),
607                    standard: &standard,
608                    names,
609                    no_builtin,
610                    pic,
611                };
612                site.survey(fuel, &mut stats)
613            };
614            if plans.is_empty() {
615                break;
616            }
617            // A plan read the body as it was, so a value it names may be the answer of a call an
618            // earlier plan in this round took away, which is `mempcpy (mempcpy (p, a, 4), b, 4)`.
619            // Every plan is renamed through what the ones before it replaced.
620            let mut renamed: HashMap<Value, Value> = HashMap::new();
621            for (inst, mut plan) in plans {
622                plan.rename(&renamed);
623                let made = apply(module, id, names, &mut texts, inst, plan);
624                for value in renamed.values_mut() {
625                    if let Some(&to) = made.get(value) {
626                        *value = to;
627                    }
628                }
629                renamed.extend(made);
630            }
631        }
632        if stats.changed() {
633            done.push((id, stats));
634        }
635    }
636    done
637}
638
639/// Whether this function calls any of the names a fold reads.
640fn mentions(func: &Func, names: &Interner, standard: &HashMap<Symbol, Symbol>) -> bool {
641    func.blocks().flat_map(|block| func.insts(block)).any(|inst| {
642        let data = &func[inst];
643        let Extra::Call(at) = data.extra else { return false };
644        data.opcode == Opcode::Call
645            && func[at].callee.is_some_and(|callee| {
646                let spelled = standard.get(&callee).copied().unwrap_or(callee);
647                SOURCES.contains(&names.resolve(spelled))
648            })
649    })
650}
651
652/// One function and everything reading it takes to answer what a call in it writes.
653struct Site<'a> {
654    /// The module it is in, which is where a string literal's bytes are.
655    module: &'a Module,
656    /// The function.
657    func: &'a Func,
658    /// Its shape, which is what a block parameter's arguments are found through.
659    cfg: &'a Cfg,
660    /// What the module allows a replacement call to look like.
661    shapes: &'a Shapes,
662    /// How many times each value is read, which is what says a result is ignored.
663    counts: &'a [u32],
664    /// What each renamed symbol was called in the source.
665    standard: &'a HashMap<Symbol, Symbol>,
666    /// The spellings, for reading a callee's name.
667    names: &'a Interner,
668    /// The names `-fno-builtin-<name>` took away.
669    no_builtin: &'a [String],
670    /// Which definitions something else may replace at load time.
671    pic: Pic,
672}
673
674impl Site<'_> {
675    /// Every call in this function that has a plan, with the plan.
676    fn survey(&self, fuel: &mut Fuel, stats: &mut Stats) -> Vec<(Inst, Plan)> {
677        let mut plans = Vec::new();
678        for block in self.func.blocks().collect::<Vec<_>>() {
679            for inst in self.func.insts(block).collect::<Vec<Inst>>() {
680                let Some(plan) = self.plan(inst) else { continue };
681                if !fuel.take() {
682                    stats.missed("call to the library folded");
683                    continue;
684                }
685                stats.optimized(match &plan {
686                    Plan::Drop => "call to the library that writes nothing removed",
687                    Plan::Answer(_) => "call to the library whose answer is known folded",
688                    Plan::Swap { .. } => "call to the library folded",
689                    Plan::Unchecked { .. } => "checking call whose check cannot fail made plain",
690                    Plan::Narrow { .. } => "rounding of a widened float done in float",
691                });
692                plans.push((inst, plan));
693            }
694        }
695        plans
696    }
697
698    /// What this call writes, where it is one of the calls this knows and the answer can be worked
699    /// out.
700    fn plan(&self, inst: Inst) -> Option<Plan> {
701        let data = &self.func[inst];
702        if data.opcode != Opcode::Call || self.func.mem_in(inst).is_some() {
703            return None;
704        }
705        // A program looking at how many characters went out is a program the count matters to, and
706        // no two of the printf family answer the same number. `strstr` is not in that position: its
707        // answer is what the call is for and the fold produces the same one.
708        let ignored = data.results().all(|result| self.counts[result.index()] == 0);
709        let name = self.called(inst)?;
710        let args: Vec<Value> = self.func[data.args].to_vec();
711        // The locked and the unlocked spellings take the same arguments and differ only in how far
712        // the fold may go, so they are an arm each with a flag rather than two bodies.
713        match name {
714            "printf" if ignored => self.printf(&args, false),
715            "printf_unlocked" if ignored => self.printf(&args, true),
716            "fprintf" if ignored => self.fprintf(&args, false),
717            "fprintf_unlocked" if ignored => self.fprintf(&args, true),
718            "fputs" if ignored => self.fputs(&args, false),
719            "fputs_unlocked" if ignored => self.fputs(&args, true),
720            // The formatted checking calls are the plain ones with a flag, and the flag says only
721            // whether a `%n` in a format the program can write to is refused, so what they write is
722            // what the plain call writes. A `v` spelling hands its arguments over in a list this
723            // cannot read, so it folds only where the format takes none of them.
724            "__printf_chk" if ignored => self.printf(args.get(1..)?, false),
725            "vprintf" | "__vprintf_chk" if ignored => {
726                let format = if name == "vprintf" { 0 } else { 1 };
727                self.printf(&[*args.get(format)?], false)
728            }
729            "__fprintf_chk" if ignored => {
730                let mut rest = vec![*args.first()?];
731                rest.extend_from_slice(args.get(2..)?);
732                self.fprintf(&rest, false)
733            }
734            "vfprintf" | "__vfprintf_chk" if ignored => {
735                let format = if name == "vfprintf" { 1 } else { 2 };
736                self.fprintf(&[*args.first()?, *args.get(format)?], false)
737            }
738            "strstr" => self.strstr(data, &args),
739            // `index` and `rindex` are the older spellings of the same two searches, and a
740            // program that wrote one of them is asking for the same answer.
741            "strchr" | "index" => self.strchr(data, &args, Side::First),
742            "strrchr" | "rindex" => self.strchr(data, &args, Side::Last),
743            "memchr" => self.memchr(data, &args),
744            "memcmp" => self.memcmp(inst, data, &args),
745            "strlen" => self.strlen(inst, data, &args),
746            "strnlen" => self.strnlen(data, &args),
747            "strcmp" => self.strcmp(data, &args),
748            "strncmp" => self.strncmp(data, &args),
749            "strcspn" => self.span(data, &args, Set::Outside),
750            "strspn" => self.span(data, &args, Set::Inside),
751            "strpbrk" => self.strpbrk(data, &args),
752            "strcpy" | "stpcpy" => self.strcpy(data, name, &args, ignored),
753            "strcat" => self.strcat(inst, data, &args),
754            "strncat" => self.strncat(data, &args),
755            "mempcpy" => self.mempcpy(data, &args, ignored),
756            "memmove" => self.memmove(data, &args),
757            "strncpy" => self.strncpy(data, &args),
758            // `bcopy` is `memmove` with the two addresses the other way round and no answer.
759            "bcopy" => {
760                let [source, dest, count] = *args else { return None };
761                if data.results().next().is_some() {
762                    return None;
763                }
764                self.moved(dest, source, count).map(|plan| match plan {
765                    Plan::Answer(_) => Plan::Drop,
766                    plan => plan,
767                })
768            }
769            "sprintf" => self.sprintf(data, &args, ignored),
770            "ceil" => self.narrow(data, &args, "ceilf"),
771            "floor" => self.narrow(data, &args, "floorf"),
772            "nearbyint" => self.narrow(data, &args, "nearbyintf"),
773            "rint" => self.narrow(data, &args, "rintf"),
774            "round" => self.narrow(data, &args, "roundf"),
775            "trunc" => self.narrow(data, &args, "truncf"),
776            "__memcpy_chk" | "__memmove_chk" | "__mempcpy_chk" | "__memset_chk" => {
777                self.memory_chk(data, name, &args, ignored)
778            }
779            "__strcpy_chk" | "__stpcpy_chk" => self.strcpy_chk(data, name, &args, ignored),
780            "__strncpy_chk" | "__stpncpy_chk" => self.strncpy_chk(data, name, &args, ignored),
781            "__strcat_chk" => self.strcat_chk(data, &args),
782            "__strncat_chk" => self.strncat_chk(data, &args),
783            "__sprintf_chk" | "__vsprintf_chk" => self.sprintf_chk(data, name, &args),
784            "__snprintf_chk" | "__vsnprintf_chk" => self.snprintf_chk(data, name, &args),
785            _ => None,
786        }
787    }
788
789    /// The type this call's one result has, where it has one and it is an integer.
790    ///
791    /// A declaration of another shape is a function of the program's own, and a number is not what
792    /// it answers.
793    fn answers(&self, data: &InstData) -> Option<Type> {
794        let mut results = data.results();
795        let ty = self.func[results.next()?].ty;
796        (results.next().is_none() && ty.is_int() && !ty.is_vector()).then_some(ty)
797    }
798
799    /// Whether this call's one result is a pointer, which every search for a place gives back.
800    fn places(&self, data: &InstData) -> bool {
801        let mut results = data.results();
802        results.next().is_some_and(|result| self.func[result].ty == Type::PTR)
803            && results.next().is_none()
804    }
805
806    /// The character a search was told to look for, which the call carries as an `int` and the
807    /// library reads as a `char`.
808    fn character(&self, value: Value) -> Option<u8> {
809        let (imm, ty) = crate::fold::evaluated(self.func, value, DEPTH)?;
810        u8::try_from(imm.signed(ty).rem_euclid(256)).ok()
811    }
812
813    /// A count of bytes the call was given, which has to be a constant that fits a `usize`.
814    ///
815    /// The source writes a small count as an `int` and the call takes a `size_t`, so what the
816    /// argument holds is a widening of the constant rather than the constant, and reading only the
817    /// argument would miss every count anyone actually writes.
818    fn count(&self, value: Value) -> Option<usize> {
819        let narrow = self.widened(value);
820        let (imm, ty) = crate::fold::evaluated(self.func, narrow, DEPTH)?;
821        // A count the source wrote as a negative number is not a count, whatever the conversion
822        // makes of it, and folding on one would be reading an object that is not there.
823        (narrow == value || imm.signed(ty) >= 0).then_some(())?;
824        usize::try_from(imm.unsigned()).ok()
825    }
826
827    /// What this value is a widening of, or the value itself where it is not one.
828    ///
829    /// Both conversions leave a non negative constant alone, so which one it was only matters for
830    /// refusing a negative one, and the caller is the one that does that.
831    fn widened(&self, value: Value) -> Value {
832        let Def::Result { inst, .. } = self.func[value].def else { return value };
833        if !matches!(self.func[inst].opcode, Opcode::SExt | Opcode::ZExt) {
834            return value;
835        }
836        self.func[self.func[inst].args].first().copied().unwrap_or(value)
837    }
838
839    /// Where a `strchr` or a `strrchr` finds its character.
840    ///
841    /// A terminator is found at the end of the string rather than not at all, which is what makes
842    /// `strchr(s, 0)` the address of the terminator and is the one place the bytes this reads and
843    /// the string it is searching are not the same length.
844    fn strchr(&self, data: &InstData, args: &[Value], side: Side) -> Option<Plan> {
845        if args.len() != 2 || self.func[args[0]].ty != Type::PTR || !self.places(data) {
846            return None;
847        }
848        let wanted = self.character(args[1])?;
849        let Some(text) = self.one(args[0]) else {
850            // A string has one terminator in it, so looking for that one from the right finds the
851            // same place as looking for it from the left, and which end the walk started at stops
852            // mattering. That is an answer even where nothing at all is known about the string.
853            return match (wanted, side) {
854                (0, Side::Last) => {
855                    self.call("strchr", vec![Argument::Have(args[0]), Argument::Char(0)])
856                }
857                _ => None,
858            };
859        };
860        let found = match (wanted, side) {
861            (0, _) => Some(text.len()),
862            (_, Side::First) => text.iter().position(|&byte| byte == wanted),
863            (_, Side::Last) => text.iter().rposition(|&byte| byte == wanted),
864        };
865        Some(Plan::Answer(match found {
866            Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
867            None => Answer::Nowhere,
868        }))
869    }
870
871    /// Where a `memchr` finds its character, which is a search over a count rather than up to a
872    /// terminator.
873    ///
874    /// So this reads the object's bytes rather than the string in it, and it refuses a count the
875    /// object does not have that many bytes for, since a call that reads past the end of what the
876    /// compiler can see is a call whose answer the compiler does not know.
877    fn memchr(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
878        (args.len() == 3).then_some(())?; // not a threshold: `memchr` takes three arguments.
879        if self.func[args[0]].ty != Type::PTR || !self.places(data) {
880            return None;
881        }
882        let wanted = self.character(args[1])?;
883        let count = self.count(args[2])?;
884        let bytes = self.raw(args[0])?;
885        let window = bytes.get(..count)?;
886        Some(Plan::Answer(match window.iter().position(|&byte| byte == wanted) {
887            Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
888            None => Answer::Nowhere,
889        }))
890    }
891
892    /// How long a string this module holds is.
893    fn strlen(&self, inst: Inst, data: &InstData, args: &[Value]) -> Option<Plan> {
894        if args.len() != 1 || self.func[args[0]].ty != Type::PTR {
895            return None;
896        }
897        self.answers(data)?;
898        if let Some(len) = self.length(args[0]) {
899            return Some(Plan::Answer(Answer::Number(i128::try_from(len).ok()?)));
900        }
901        if let Some(text) = self.stored(inst, args[0]) {
902            return Some(Plan::Answer(Answer::Number(i128::try_from(text.len()).ok()?)));
903        }
904        // `strlen("hello world" + (x & 7))` is eleven less the step, because a step of no more than
905        // the length lands on a byte of the string or on its terminator and there is no other
906        // terminator before the end. gcc 16 folds it the same way.
907        let Def::Result { inst, .. } = self.func[args[0]].def else { return None };
908        if self.func[inst].opcode != Opcode::PtrAdd {
909            return None;
910        }
911        let &[base, step] = &self.func[self.func[inst].args] else { return None };
912        let len = u64::try_from(self.literal(base)?.len()).ok()?;
913        if self.largest(step)? > u128::from(len) {
914            return None;
915        }
916        Some(Plan::Answer(Answer::Less { len, step }))
917    }
918
919    /// The string at this address in a local array, as what ran in front of the call wrote it.
920    ///
921    /// `builtins/strlen.c` writes "nts" and its terminator into a `char str[8]` a byte at a time
922    /// and asks how long it is, and `builtins/strcat.c` fills its array with `memset` and `strcpy`
923    /// before every `strcat`, and gcc 16 answers both from what was written. The walk goes back
924    /// from the call and keeps the last byte written at each place. It carries on into the block
925    /// in front where the block it is in has only that one way in, which is what a `do { } while
926    /// (0)` around the writes leaves behind, and it stops at the first thing that could have
927    /// written the array some other way. What it has then is enough only where it reaches a
928    /// terminator from the place asked about without a gap.
929    fn stored(&self, call: Inst, value: Value) -> Option<Vec<u8>> {
930        let (base, offset) = self.address(value)?;
931        let bytes = self.before(call, base)?;
932        let mut text = Vec::new();
933        for at in (offset..).take(bytes.len()) {
934            match *bytes.get(&at)? {
935                0 => return Some(text),
936                byte => text.push(byte),
937            }
938        }
939        None
940    }
941
942    /// The bytes of the local array at `base` the walk in front of this call found written, by
943    /// their place in the array.
944    fn before(&self, call: Inst, base: Value) -> Option<HashMap<i128, u8>> {
945        let size = self.extent(base)?;
946        let mut bytes: HashMap<i128, u8> = HashMap::new();
947        let mut block = self.func.block_of(call)?;
948        let mut from = Some(call);
949        'walk: for _ in 0..CHAIN {
950            let insts: Vec<Inst> = match from.take() {
951                Some(call) => self
952                    .func
953                    .insts_backwards(block)
954                    .skip_while(|&inst| inst != call)
955                    .skip(1)
956                    .collect(),
957                None => self.func.insts_backwards(block).collect(),
958            };
959            for inst in insts {
960                if self.wrote(inst, base, size, &mut bytes).is_none() {
961                    break 'walk;
962                }
963            }
964            let Some(pred) = self.only_way_in(block) else { break };
965            block = pred;
966        }
967        Some(bytes)
968    }
969
970    /// The one block control can have come from into this one, leaving out any that calls a
971    /// function that does not come back.
972    ///
973    /// `if (memcmp (...) != 0) abort ();` is a join in front of whatever comes next until the
974    /// branches are cleaned up, which is after this pass, and control reaching the join cannot
975    /// have come through the arm that called `abort`, so the walk goes on up the other arm.
976    fn only_way_in(&self, block: Block) -> Option<Block> {
977        let mut live = self
978            .cfg
979            .predecessors(block)
980            .iter()
981            .copied()
982            .filter(|&pred| !self.func.insts(pred).any(|inst| self.never_back(inst)));
983        let first = live.next()?;
984        live.next().is_none().then_some(first)
985    }
986
987    /// Whether this is a call control does not come back from, by the callee's own attribute or
988    /// because it is a declaration of `abort` or `exit`, which gcc knows the same way.
989    fn never_back(&self, inst: Inst) -> bool {
990        if self.func[inst].opcode != Opcode::Call {
991            return false;
992        }
993        let Extra::Call(at) = self.func[inst].extra else { return false };
994        let Some(callee) = self.func[at].callee else { return false };
995        let Some(SymbolRef::Func(id)) = self.module.lookup(callee) else { return false };
996        let target = &self.module[id];
997        target.attrs.set.contains(AttrSet::NORETURN)
998            || (target.entry().is_none()
999                && matches!(self.called(inst), Some("abort" | "exit" | "_Exit" | "quick_exit")))
1000    }
1001
1002    /// The `count` bytes from this address, out of a constant object or out of what was written
1003    /// into a local array in front of the call, with no gap and no terminator stopping them.
1004    fn held(&self, call: Inst, value: Value, count: usize) -> Option<Vec<u8>> {
1005        if let Some(bytes) = self.raw(value) {
1006            return Some(bytes.get(..count)?.to_vec());
1007        }
1008        let (base, offset) = self.address(value)?;
1009        let bytes = self.before(call, base)?;
1010        (offset..).take(count).map(|at| bytes.get(&at).copied()).collect()
1011    }
1012
1013    /// Puts what this instruction wrote into the local array at `base` into `bytes`, under the
1014    /// places nothing later wrote, or `None` where it may have written the array in a way this
1015    /// cannot read.
1016    ///
1017    /// A byte stored, a `memset` of a known byte, a `memcpy` out of an object whose bytes the
1018    /// module holds and a `strcpy` of a string it holds are read. Anything else that writes memory
1019    /// ends the walk, apart from one of those into another local, since two locals are two
1020    /// objects.
1021    fn wrote(
1022        &self,
1023        inst: Inst,
1024        base: Value,
1025        size: u64,
1026        bytes: &mut HashMap<i128, u8>,
1027    ) -> Option<()> {
1028        let data = &self.func[inst];
1029        if !data.opcode.writes_memory() {
1030            return Some(());
1031        }
1032        let args = &self.func[data.args];
1033        let (to, written) = match data.opcode {
1034            Opcode::Store => {
1035                let &[byte, to] = args else { return None };
1036                if self.func[byte].ty != Type::int(8) {
1037                    return None;
1038                }
1039                (to, vec![u8::try_from(self.number(byte)?).ok()?])
1040            }
1041            Opcode::Call => {
1042                let &to = args.first()?;
1043                let count =
1044                    || self.number(*args.get(2)?).filter(|&count| count <= u128::from(size));
1045                let written = match self.called(inst)? {
1046                    "memset" => {
1047                        vec![self.character(*args.get(1)?)?; usize::try_from(count()?).ok()?]
1048                    }
1049                    "memcpy" => {
1050                        let count = usize::try_from(count()?).ok()?;
1051                        self.raw(*args.get(1)?)?.get(..count)?.to_vec()
1052                    }
1053                    "strcpy" => {
1054                        let mut text = self.one(*args.get(1)?)?;
1055                        text.push(0);
1056                        text
1057                    }
1058                    // Calls that only read, which an earlier comparison on the same array is.
1059                    "memcmp" | "memchr" | "strcmp" | "strncmp" | "strlen" | "strchr" => {
1060                        return Some(());
1061                    }
1062                    _ => return None,
1063                };
1064                (to, written)
1065            }
1066            _ => return None,
1067        };
1068        let (root, at) = self.address(to)?;
1069        if root != base {
1070            return self.local(root).then_some(());
1071        }
1072        let end = at.checked_add(i128::try_from(written.len()).ok()?)?;
1073        if at < 0 || end > i128::from(size) {
1074            return None;
1075        }
1076        for (place, byte) in (at..).zip(written) {
1077            bytes.entry(place).or_insert(byte);
1078        }
1079        Some(())
1080    }
1081
1082    /// The name of the library function this call is to, where the program has not taken the
1083    /// compiler's knowledge of it away.
1084    fn called(&self, inst: Inst) -> Option<&str> {
1085        let Extra::Call(at) = self.func[inst].extra else { return None };
1086        let callee = self.func[at].callee?;
1087        let name = self.names.resolve(self.standard.get(&callee).copied().unwrap_or(callee));
1088        (!self.no_builtin.iter().any(|it| it == name)).then_some(name)
1089    }
1090
1091    /// How many bytes the local array at this address has, or `None` where it is not one.
1092    fn extent(&self, value: Value) -> Option<u64> {
1093        let Def::Result { inst, .. } = self.func[value].def else { return None };
1094        let data = &self.func[inst];
1095        if data.opcode != Opcode::Alloca || !self.func[data.args].is_empty() {
1096            return None;
1097        }
1098        let Extra::Mem(mem) = data.extra else { return None };
1099        Some(self.func[mem].size)
1100    }
1101
1102    /// The same, stopping at a count.
1103    ///
1104    /// A string with no terminator inside the count is the count, and that needs the object's bytes
1105    /// rather than the string in it, because there may be no string in it at all.
1106    fn strnlen(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1107        if args.len() != 2 || self.func[args[0]].ty != Type::PTR {
1108            return None;
1109        }
1110        let ty = self.answers(data)?;
1111        // A string with its terminator inside the object is read no further than the terminator
1112        // whatever the count is, so the answer is the smaller of the two, and any count at all is
1113        // one the call could have been given. That includes a count the source wrote as a negative
1114        // number, which is a very large one once it is a `size_t`.
1115        if let Some(text) = self.literal(args[0]) {
1116            let len = u64::try_from(text.len()).ok()?;
1117            if let Some((imm, _)) = crate::fold::evaluated(self.func, args[1], DEPTH) {
1118                let least = imm.unsigned().min(u128::from(len));
1119                return Some(Plan::Answer(Answer::Number(i128::try_from(least).ok()?)));
1120            }
1121            // An empty string is nothing to count, so the count does not matter.
1122            if len == 0 {
1123                return Some(Plan::Answer(Answer::Number(0)));
1124            }
1125            // Otherwise the smaller of the two has to be worked out when the program runs, and the
1126            // count and the answer have to be the same type for that to be one comparison.
1127            (self.func[args[1]].ty == ty).then_some(())?;
1128            return Some(Plan::Answer(Answer::Least { count: args[1], len }));
1129        }
1130        let count = self.count(args[1])?;
1131        let bytes = self.raw(args[0])?;
1132        let window = bytes.get(..count.min(bytes.len()))?;
1133        let len = match window.iter().position(|&byte| byte == 0) {
1134            Some(at) => at,
1135            // Nothing terminated it inside the window, so the answer is the count only where the
1136            // window was the whole count.
1137            None if window.len() == count => count,
1138            None => return None,
1139        };
1140        Some(Plan::Answer(Answer::Number(i128::try_from(len).ok()?)))
1141    }
1142
1143    /// How two objects compare over a count of bytes the call was given.
1144    ///
1145    /// Unlike the string comparisons there is no terminator, so every byte up to the count has to
1146    /// be known, and it may be known from a constant object or from what was written into a local
1147    /// array in front of the call. `builtins/memcmp.c` asks both, and gcc 16 folds both. The answer
1148    /// is the sign of the first byte that differs read as an `unsigned char`, which is all the
1149    /// standard promises. Where only one side is known a count of one is still an answer, since
1150    /// that one byte is the whole comparison.
1151    fn memcmp(&self, call: Inst, data: &InstData, args: &[Value]) -> Option<Plan> {
1152        (args.len() == 3).then_some(())?; // not a threshold: `memcmp` takes three arguments.
1153        if self.func[args[0]].ty != Type::PTR || self.func[args[1]].ty != Type::PTR {
1154            return None;
1155        }
1156        let ty = self.answers(data)?;
1157        let count = self.count(args[2])?;
1158        // Told to read no bytes it reads neither object, so the answer holds whatever they hold.
1159        if count == 0 {
1160            return Some(Plan::Answer(Answer::Number(0)));
1161        }
1162        match (self.held(call, args[0], count), self.held(call, args[1], count)) {
1163            (Some(left), Some(right)) => {
1164                let differs = left.iter().zip(&right).find(|(this, that)| this != that);
1165                let sign = differs.map_or(0, |(this, that)| if this < that { -1 } else { 1 });
1166                Some(Plan::Answer(Answer::Number(sign)))
1167            }
1168            (Some(known), None) => self.byte(ty, &known, args[1], true, count),
1169            (None, Some(known)) => self.byte(ty, &known, args[0], false, count),
1170            (None, None) => None,
1171        }
1172    }
1173
1174    /// How two strings this module holds compare.
1175    fn strcmp(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1176        (args.len() == 2).then_some(())?;
1177        self.compared(data, args, usize::MAX)
1178    }
1179
1180    /// The same over a count the call was given, which has to be a constant.
1181    fn strncmp(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1182        (args.len() == 3).then_some(())?; // not a threshold: `strncmp` takes three arguments.
1183        let count = self.count(args[2])?;
1184        self.compared(data, args, count)
1185    }
1186
1187    /// The comparison both of them are, over however many bytes each is allowed to read.
1188    ///
1189    /// The sign is what the standard promises and the magnitude is not, so where both strings are
1190    /// known this answers one of minus one, zero and one, which is what gcc leaves behind as well.
1191    /// Where only one of them is known there is still an answer in the two cases the first byte
1192    /// settles, and that one is a read rather than a constant.
1193    fn compared(&self, data: &InstData, args: &[Value], bound: usize) -> Option<Plan> {
1194        if self.func[args[0]].ty != Type::PTR || self.func[args[1]].ty != Type::PTR {
1195            return None;
1196        }
1197        let ty = self.answers(data)?;
1198        // A comparison told to read no bytes reads neither string, so the answer is the same
1199        // whatever the two of them hold, and holds even where neither is there to be read.
1200        if bound == 0 {
1201            return Some(Plan::Answer(Answer::Number(0)));
1202        }
1203        match (self.one(args[0]), self.one(args[1])) {
1204            (Some(left), Some(right)) => {
1205                Some(Plan::Answer(Answer::Number(walk(&left, &right, bound))))
1206            }
1207            (Some(known), None) => self.byte(ty, &known, args[1], true, bound),
1208            (None, Some(known)) => self.byte(ty, &known, args[0], false, bound),
1209            (None, None) => None,
1210        }
1211    }
1212
1213    /// The comparison a string this module holds makes against one nothing is known about.
1214    ///
1215    /// Only the first byte of the other string can be read here, so this is an answer in the two
1216    /// cases where that byte is the whole comparison: a count of one, which is that byte and
1217    /// nothing else, and a known string that is empty, whose terminator stops the walk however
1218    /// many bytes the count allowed.
1219    fn byte(
1220        &self,
1221        ty: Type,
1222        known: &[u8],
1223        other: Value,
1224        leading: bool,
1225        bound: usize,
1226    ) -> Option<Plan> {
1227        (bound == 1 || known.is_empty()).then_some(())?;
1228        // The answer is a byte widened into the type the call was declared with, and a type no
1229        // wider than a byte is a declaration this has no room to answer in.
1230        (ty.bits() > 8).then_some(())?;
1231        let against = known.first().copied().unwrap_or(0);
1232        Some(Plan::Answer(Answer::Byte { of: other, against, leading }))
1233    }
1234
1235    /// How far into the first string the second one's characters start, or stop.
1236    ///
1237    /// `strcspn` walks while the character is outside the set and `strspn` walks while it is
1238    /// inside, which is one walk with the test turned round, and the shape rules fall out of it:
1239    /// nothing is outside an empty set, so `strspn(s, "")` is zero, and everything is, so
1240    /// `strcspn(s, "")` is the length of `s`.
1241    fn span(&self, data: &InstData, args: &[Value], set: Set) -> Option<Plan> {
1242        if args.len() != 2
1243            || self.func[args[0]].ty != Type::PTR
1244            || self.func[args[1]].ty != Type::PTR
1245        {
1246            return None;
1247        }
1248        let ty = self.answers(data)?;
1249        // An empty first string is no bytes to walk over, whatever the set is, and that is the
1250        // answer `strcspn("", s)` wants where nothing is known about `s`.
1251        if self.one(args[0]).is_some_and(|text| text.is_empty()) {
1252            return Some(Plan::Answer(Answer::Number(0)));
1253        }
1254        let accept = self.one(args[1])?;
1255        // Both walks are the same walk with the test turned round, and an empty set needs no arm of
1256        // its own here, because nothing is inside one and so the walk stops at once or not at all.
1257        if let Some(text) = self.one(args[0]) {
1258            let len = text
1259                .iter()
1260                .position(|byte| accept.contains(byte) != matches!(set, Set::Inside))
1261                .unwrap_or(text.len());
1262            return Some(Plan::Answer(Answer::Number(i128::try_from(len).ok()?)));
1263        }
1264        // Nothing is known about the string, so only the shape rules are left.
1265        accept.is_empty().then_some(())?;
1266        match set {
1267            Set::Inside => Some(Plan::Answer(Answer::Number(0))),
1268            // A number the call gives back and a number `strlen` gives back have to be the same
1269            // width, since what reads the first is going to read the second and nothing here writes
1270            // a conversion.
1271            Set::Outside => {
1272                let (_, signature) = self.shapes.get("strlen")?;
1273                signature.return_types().eq([ty]).then_some(())?;
1274                self.call("strlen", vec![Argument::Have(args[0])])
1275            }
1276        }
1277    }
1278
1279    /// Where the first character of one string that is in the other is.
1280    fn strpbrk(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1281        if args.len() != 2 || self.func[args[0]].ty != Type::PTR || !self.places(data) {
1282            return None;
1283        }
1284        if self.func[args[1]].ty != Type::PTR {
1285            return None;
1286        }
1287        let accept = self.one(args[1])?;
1288        // Nothing is in an empty set, so the search runs off the end of any string at all.
1289        if accept.is_empty() {
1290            return Some(Plan::Answer(Answer::Nowhere));
1291        }
1292        match self.one(args[0]) {
1293            Some(text) => {
1294                Some(Plan::Answer(match text.iter().position(|byte| accept.contains(byte)) {
1295                    Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
1296                    None => Answer::Nowhere,
1297                }))
1298            }
1299            // A set of one character is a search for that character, which is the same fold
1300            // `strstr` of a needle of one character gets.
1301            None => match accept.as_slice() {
1302                [one] => self.call("strchr", vec![Argument::Have(args[0]), Argument::Char(*one)]),
1303                _ => None,
1304            },
1305        }
1306    }
1307
1308    /// Where a `strstr` finds what it was told to look for.
1309    ///
1310    /// The three folds gcc has for it, and the order matters: two strings this module holds are an
1311    /// answer, and a haystack nothing is known about is a search for a character where the needle is
1312    /// one character long.
1313    fn strstr(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1314        if args.len() != 2 {
1315            return None;
1316        }
1317        let (haystack, needle) = (args[0], args[1]);
1318        if self.func[haystack].ty != Type::PTR || self.func[needle].ty != Type::PTR {
1319            return None;
1320        }
1321        // A declaration of another shape is a function of the program's own, and the answer this
1322        // produces is a pointer whatever the program said the call gives back.
1323        let mut results = data.results();
1324        if !results.next().is_some_and(|result| self.func[result].ty == Type::PTR)
1325            || results.next().is_some()
1326        {
1327            return None;
1328        }
1329        let needle = self.one(needle)?;
1330        // The empty string is found at once, wherever it is looked for and whatever is there.
1331        if needle.is_empty() {
1332            return Some(Plan::Answer(Answer::Along(haystack, 0)));
1333        }
1334        match self.one(haystack) {
1335            Some(hay) => Some(Plan::Answer(match at(&hay, &needle) {
1336                Some(found) => Answer::Along(haystack, u64::try_from(found).ok()?),
1337                None => Answer::Nowhere,
1338            })),
1339            // A needle of one character is a search for that character, which is a smaller function
1340            // and is worth doing wherever the haystack came from.
1341            None => match needle.as_slice() {
1342                [one] => self.call("strchr", vec![Argument::Have(haystack), Argument::Char(*one)]),
1343                _ => None,
1344            },
1345        }
1346    }
1347
1348    /// What a `printf` writes.
1349    fn printf(&self, args: &[Value], quiet: bool) -> Option<Plan> {
1350        let format = self.one(*args.first()?)?;
1351        match args.len() {
1352            1 => self.plain(&format, None, quiet),
1353            2 if format == b"%s\n" && !quiet && self.func[args[1]].ty == Type::PTR => {
1354                self.call("puts", vec![Argument::Have(args[1])])
1355            }
1356            2 if format == b"%c" && !quiet && self.func[args[1]].ty == int() => {
1357                self.call("putchar", vec![Argument::Have(args[1])])
1358            }
1359            // The same question asked again of the argument, because what `printf("%s", p)` writes
1360            // is what `printf(p)` writes for a `p` holding no `%`. A `p` that does hold one is left
1361            // alone here and folded by gcc, which is a missed fold and not a wrong answer.
1362            2 if format == b"%s" => self.plain(&self.one(args[1])?, None, quiet),
1363            _ => None,
1364        }
1365    }
1366
1367    /// What an `fprintf` writes.
1368    fn fprintf(&self, args: &[Value], quiet: bool) -> Option<Plan> {
1369        let stream = *args.first()?;
1370        if self.func[stream].ty != Type::PTR {
1371            return None;
1372        }
1373        let format = self.one(*args.get(1)?)?;
1374        match args.len() {
1375            2 => self.plain(&format, Some((args[1], stream)), quiet),
1376            3 if format == b"%c" && !quiet && self.func[args[2]].ty == int() => {
1377                self.call("fputc", vec![Argument::Have(args[2]), Argument::Have(stream)])
1378            }
1379            // Whatever is known about the argument, which is the fold `printf` cannot have: this
1380            // one holds the stream, so the call it leaves behind is one the program could have
1381            // written for itself.
1382            3 if format == b"%s" && self.func[args[2]].ty == Type::PTR => {
1383                match self.strings(args[2]) {
1384                    Some(candidates) => self.string(&candidates, args[2], stream, quiet),
1385                    None if quiet => None,
1386                    None => {
1387                        self.call("fputs", vec![Argument::Have(args[2]), Argument::Have(stream)])
1388                    }
1389                }
1390            }
1391            _ => None,
1392        }
1393    }
1394
1395    /// What an `fputs` writes.
1396    fn fputs(&self, args: &[Value], quiet: bool) -> Option<Plan> {
1397        if args.len() != 2 {
1398            return None;
1399        }
1400        let (text, stream) = (args[0], args[1]);
1401        if self.func[text].ty != Type::PTR || self.func[stream].ty != Type::PTR {
1402            return None;
1403        }
1404        self.string(&self.strings(text)?, text, stream, quiet)
1405    }
1406
1407    /// What a format holding no `%` writes, given the stream to write it to or nothing.
1408    ///
1409    /// The empty case comes first and is the one an unlocked spelling is allowed, because a call
1410    /// writing nothing is removed without naming any function at all.
1411    fn plain(&self, format: &[u8], stream: Option<(Value, Value)>, quiet: bool) -> Option<Plan> {
1412        if format.is_empty() {
1413            return Some(Plan::Drop);
1414        }
1415        if quiet || format.contains(&b'%') {
1416            return None;
1417        }
1418        match (format, stream) {
1419            ([one], Some((_, stream))) => {
1420                self.call("fputc", vec![Argument::Char(*one), Argument::Have(stream)])
1421            }
1422            // Anything longer takes the whole format, since `fwrite` is told how many bytes to
1423            // write and does not go looking for a terminator.
1424            (_, Some((text, stream))) => self.fwrite(Argument::Have(text), format.len(), stream),
1425            ([one], None) => self.call("putchar", vec![Argument::Char(*one)]),
1426            // `puts` writes a newline of its own, so what it has to be given is the format without
1427            // its last character, and that is a string the module does not hold yet.
1428            (_, None) => {
1429                let (&last, rest) = format.split_last()?;
1430                match last {
1431                    b'\n' => self.call("puts", vec![Argument::Text(rest.to_vec())]),
1432                    _ => None,
1433                }
1434            }
1435        }
1436    }
1437
1438    /// What writing this string to this stream is, given every string the pointer may point at.
1439    ///
1440    /// The candidates have to agree on their length, because the length is what decides which call
1441    /// this becomes. They need not agree on their contents unless the length is one, where the
1442    /// character itself is an argument.
1443    fn string(
1444        &self,
1445        candidates: &[Vec<u8>],
1446        text: Value,
1447        stream: Value,
1448        quiet: bool,
1449    ) -> Option<Plan> {
1450        let first = candidates.first()?;
1451        if candidates.iter().any(|it| it.len() != first.len()) {
1452            return None;
1453        }
1454        if first.is_empty() {
1455            return Some(Plan::Drop);
1456        }
1457        if quiet {
1458            return None;
1459        }
1460        match first.as_slice() {
1461            [one] if candidates.iter().all(|it| it[0] == *one) => {
1462                self.call("fputc", vec![Argument::Char(*one), Argument::Have(stream)])
1463            }
1464            // A length of one that two candidates disagree about is an `fwrite` of one byte, since
1465            // the byte itself is not a constant here and the pointer is what has to be written.
1466            // That is gcc's answer too, and its output for this is a conditional move feeding an
1467            // `fwrite` of one.
1468            _ => self.fwrite(Argument::Have(text), first.len(), stream),
1469        }
1470    }
1471
1472    /// An `fwrite` of that many bytes from that address.
1473    fn fwrite(&self, text: Argument, bytes: usize, stream: Value) -> Option<Plan> {
1474        let len = u64::try_from(bytes).ok()?;
1475        self.call(
1476            "fwrite",
1477            vec![text, Argument::Count(1), Argument::Count(len), Argument::Have(stream)],
1478        )
1479    }
1480
1481    /// `strcpy` and `stpcpy` of a string whose length is known, which copy that many bytes and a
1482    /// terminator and are `memcpy` of that many.
1483    ///
1484    /// `stpcpy` answers the end of the copy rather than the start, which is the one difference
1485    /// between the two, so where nothing reads its answer it is `strcpy` whatever the string is.
1486    fn strcpy(&self, data: &InstData, name: &str, args: &[Value], ignored: bool) -> Option<Plan> {
1487        let [dest, source] = *args else { return None };
1488        if !self.places(data) {
1489            return None;
1490        }
1491        let end = name == "stpcpy";
1492        if end && ignored {
1493            return self.unchecked(data, "strcpy", &[]);
1494        }
1495        let len = self.length(source)?;
1496        let (callee, signature) = self.shapes.get("memcpy")?;
1497        let args =
1498            vec![Argument::Have(dest), Argument::Have(source), Argument::Count(len as u64 + 1)];
1499        let answer = end.then_some(Answer::Along(dest, len as u64));
1500        Some(Plan::Swap { callee, signature, args, answer })
1501    }
1502
1503    /// `strcat` and `strncat` that append nothing, which answer where they were told to append.
1504    ///
1505    /// Nothing is appended where the string is empty or where the count is zero, and neither call
1506    /// reads the destination before it knows that.
1507    fn nothing(&self, data: &InstData, args: &[Value], count: Option<Value>) -> Option<Plan> {
1508        let [dest, source] = *args else { return None };
1509        let nothing = self.one(source).is_some_and(|text| text.is_empty())
1510            || count.is_some_and(|count| self.number(count) == Some(0));
1511        (nothing && self.places(data)).then_some(Plan::Answer(Answer::Along(dest, 0)))
1512    }
1513
1514    /// `strcat` that appends nothing, and `strcat` of a string of known length onto one whose
1515    /// length the writes in front of the call say, which is a copy of the string and its
1516    /// terminator to where the old terminator was. gcc 16 makes that of every `strcat` in
1517    /// `builtins/strcat.c`.
1518    fn strcat(&self, inst: Inst, data: &InstData, args: &[Value]) -> Option<Plan> {
1519        if let Some(plan) = self.nothing(data, args, None) {
1520            return Some(plan);
1521        }
1522        let [dest, source] = *args else { return None };
1523        if !self.places(data) {
1524            return None;
1525        }
1526        let len = u64::try_from(self.length(source)?).ok()?;
1527        let before = u64::try_from(self.stored(inst, dest)?.len()).ok()?;
1528        let (callee, signature) = self.shapes.get("memcpy")?;
1529        let args =
1530            vec![Argument::At(dest, before), Argument::Have(source), Argument::Count(len + 1)];
1531        Some(Plan::Swap { callee, signature, args, answer: Some(Answer::Along(dest, 0)) })
1532    }
1533
1534    /// `strncat` whose count is no limit on a string whose length is known, which is `strcat`.
1535    fn strncat(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1536        let [dest, source, count] = *args else { return None };
1537        if let Some(plan) = self.nothing(data, &[dest, source], Some(count)) {
1538            return Some(plan);
1539        }
1540        let len = self.one(source)?.len() as u128;
1541        (self.number(count)? >= len).then(|| self.unchecked(data, "strcat", &[2]))?
1542    }
1543
1544    /// `memmove`, which copies nothing where the count is zero and is `memcpy` where the two
1545    /// places cannot overlap.
1546    fn memmove(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1547        let [dest, source, count] = *args else { return None };
1548        if !self.places(data) {
1549            return None;
1550        }
1551        self.moved(dest, source, count)
1552    }
1553
1554    /// What a move of that many bytes from the source to the destination is, where it is anything
1555    /// but itself.
1556    ///
1557    /// A move that cannot overlap is a copy. That is so where it is one byte, since one byte is
1558    /// read before it is written; where the source is a read only object, since the destination is
1559    /// written and a read only object is not; and where either side is a local the other is not,
1560    /// since two objects do not overlap. `builtins/memmove.c` and `builtins/memmove-2.c` are all
1561    /// three, and gcc 16 makes a `memcpy` or plain loads and stores of each. A local and a pointer
1562    /// read from somewhere are not two objects, since the pointer may be the local's address.
1563    fn moved(&self, dest: Value, source: Value, count: Value) -> Option<Plan> {
1564        if self.number(count) == Some(0) {
1565            return Some(Plan::Answer(Answer::Along(dest, 0)));
1566        }
1567        let (to, _) = self.address(dest)?;
1568        let (from, _) = self.address(source)?;
1569        let apart = self.largest(count).is_some_and(|count| count <= 1)
1570            || self.fixed(from)
1571            || (to != from && self.object(to) && self.object(from))
1572                && (self.local(to) || self.local(from));
1573        apart.then(|| self.copy(dest, source, count))?
1574    }
1575
1576    /// `strncpy` that copies nothing, which answers its destination, and `strncpy` whose count is
1577    /// no more than the source's length and its terminator, which pads with nothing and is a
1578    /// `memcpy` of the count. A longer count pads the rest with zeros and is left as a call.
1579    fn strncpy(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1580        let [dest, source, count] = *args else { return None };
1581        if !self.places(data) {
1582            return None;
1583        }
1584        let number = self.number(count)?;
1585        if number == 0 {
1586            return Some(Plan::Answer(Answer::Along(dest, 0)));
1587        }
1588        let len = u128::try_from(self.length(source)?).ok()?;
1589        (number <= len + 1).then(|| self.copy(dest, source, count))?
1590    }
1591
1592    /// A `memcpy` of that many bytes, answering the destination.
1593    fn copy(&self, dest: Value, source: Value, count: Value) -> Option<Plan> {
1594        let (callee, signature) = self.shapes.get("memcpy")?;
1595        let args = vec![Argument::Have(dest), Argument::Have(source), Argument::Have(count)];
1596        Some(Plan::Swap { callee, signature, args, answer: None })
1597    }
1598
1599    /// Whether this is the address of an object rather than a pointer that could be anywhere.
1600    fn object(&self, value: Value) -> bool {
1601        self.local(value)
1602            || matches!(self.func[value].def, Def::Result { inst, .. } if self.func[inst].opcode == Opcode::GlobalAddr)
1603    }
1604
1605    /// Whether this is the address of a local array, which no other object overlaps.
1606    fn local(&self, value: Value) -> bool {
1607        matches!(self.func[value].def, Def::Result { inst, .. } if self.func[inst].opcode == Opcode::Alloca)
1608    }
1609
1610    /// Whether this is the address of a read only object whose definition the link cannot swap for
1611    /// one that is not.
1612    fn fixed(&self, value: Value) -> bool {
1613        let Def::Result { inst, .. } = self.func[value].def else { return false };
1614        if self.func[inst].opcode != Opcode::GlobalAddr {
1615            return false;
1616        }
1617        let Extra::Symbol(name) = self.func[inst].extra else { return false };
1618        let Some(SymbolRef::Global(id)) = self.module.lookup(name) else { return false };
1619        let global = &self.module[id];
1620        global.constant && vouched(global, self.pic)
1621    }
1622
1623    /// `mempcpy`, which is `memcpy` answering the end of the copy rather than the start.
1624    ///
1625    /// Where nothing reads the answer the two are the same call. Where something does, the end is
1626    /// the start and the count, which is an address this can write only where the count is known.
1627    fn mempcpy(&self, data: &InstData, args: &[Value], ignored: bool) -> Option<Plan> {
1628        let [dest, source, count] = *args else { return None };
1629        if ignored {
1630            return self.unchecked(data, "memcpy", &[]);
1631        }
1632        let along = u64::try_from(self.number(count)?).ok()?;
1633        if !self.places(data) {
1634            return None;
1635        }
1636        let (callee, signature) = self.shapes.get("memcpy")?;
1637        let args = vec![Argument::Have(dest), Argument::Have(source), Argument::Have(count)];
1638        Some(Plan::Swap { callee, signature, args, answer: Some(Answer::Along(dest, along)) })
1639    }
1640
1641    /// `sprintf` of a format with nothing to convert, or of `"%s"` and one string, which writes
1642    /// the string and is `strcpy` of it.
1643    ///
1644    /// The count `sprintf` answers is the length of what it wrote and `strcpy` answers something
1645    /// else, so a program reading it needs that length to be one the compiler knows.
1646    fn sprintf(&self, data: &InstData, args: &[Value], ignored: bool) -> Option<Plan> {
1647        let (&dest, &format) = (args.first()?, args.get(1)?);
1648        let text = self.one(format)?;
1649        let source = match *args {
1650            [_, _] if !text.contains(&b'%') => format,
1651            [_, _, arg] if text == b"%s" && self.func[arg].ty == Type::PTR => arg,
1652            _ => return None,
1653        };
1654        let answer = if ignored {
1655            None
1656        } else {
1657            self.answers(data)?;
1658            Some(Answer::Number(i128::try_from(self.one(source)?.len()).ok()?))
1659        };
1660        let (callee, signature) = self.shapes.get("strcpy")?;
1661        Some(Plan::Swap {
1662            callee,
1663            signature,
1664            args: vec![Argument::Have(dest), Argument::Have(source)],
1665            answer,
1666        })
1667    }
1668
1669    /// `__memcpy_chk` and the three beside it, which are the plain call where the count is known to
1670    /// fit the object.
1671    ///
1672    /// `__mempcpy_chk` answers the end of the copy and `__memcpy_chk` the start, so where nothing
1673    /// reads the answer and the check has to stay, it stays on the call that does not work one out.
1674    fn memory_chk(
1675        &self,
1676        data: &InstData,
1677        name: &str,
1678        args: &[Value],
1679        ignored: bool,
1680    ) -> Option<Plan> {
1681        let [_, _, count, size] = *args else { return None };
1682        if self.fits(count, size) {
1683            return self.unchecked(data, plain(name), &[3]);
1684        }
1685        (name == "__mempcpy_chk" && ignored).then(|| self.unchecked(data, "__memcpy_chk", &[]))?
1686    }
1687
1688    /// `__strcpy_chk` and `__stpcpy_chk`, which are the plain call where the string and its
1689    /// terminator are known to fit.
1690    ///
1691    /// Where they are not known to, a string whose length is known is still a count, so
1692    /// `__strcpy_chk` becomes the `__memcpy_chk` of that many bytes and the library checks a number
1693    /// rather than walking a string to find one. `__stpcpy_chk` whose answer nothing reads becomes
1694    /// `__strcpy_chk` for the same reason `stpcpy` becomes `strcpy`.
1695    fn strcpy_chk(
1696        &self,
1697        data: &InstData,
1698        name: &str,
1699        args: &[Value],
1700        ignored: bool,
1701    ) -> Option<Plan> {
1702        let [dest, source, size] = *args else { return None };
1703        let end = name == "__stpcpy_chk";
1704        let fits = self.unknown(size)
1705            || self.longest(source).zip(self.number(size)).is_some_and(|(len, size)| len < size);
1706        if fits {
1707            return self.unchecked(data, if end && !ignored { "stpcpy" } else { "strcpy" }, &[2]);
1708        }
1709        if end {
1710            return ignored.then(|| self.unchecked(data, "__strcpy_chk", &[]))?;
1711        }
1712        let len = self.one(source)?.len() as u64;
1713        let args = vec![
1714            Argument::Have(dest),
1715            Argument::Have(source),
1716            Argument::Count(len + 1),
1717            Argument::Have(size),
1718        ];
1719        self.call("__memcpy_chk", args)
1720    }
1721
1722    /// `__strncpy_chk` and `__stpncpy_chk`, which write exactly as many bytes as they were told to
1723    /// and are the plain call where that many fit.
1724    fn strncpy_chk(
1725        &self,
1726        data: &InstData,
1727        name: &str,
1728        args: &[Value],
1729        ignored: bool,
1730    ) -> Option<Plan> {
1731        let [_, _, count, size] = *args else { return None };
1732        let end = name == "__stpncpy_chk";
1733        if self.fits(count, size) {
1734            return self.unchecked(data, if end && !ignored { "stpncpy" } else { "strncpy" }, &[3]);
1735        }
1736        (end && ignored).then(|| self.unchecked(data, "__strncpy_chk", &[]))?
1737    }
1738
1739    /// `__strcat_chk`, which appends nothing where the string is empty and is the plain call only
1740    /// where nothing is known about the object.
1741    ///
1742    /// What it appends to is a string whose length is not known here, so no size is enough.
1743    fn strcat_chk(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1744        let [dest, source, size] = *args else { return None };
1745        if let Some(plan) = self.nothing(data, &[dest, source], None) {
1746            return Some(plan);
1747        }
1748        self.unknown(size).then(|| self.unchecked(data, "strcat", &[2]))?
1749    }
1750
1751    /// `__strncat_chk`, which is `__strcat_chk` where the count is no limit on a string whose length
1752    /// is known.
1753    fn strncat_chk(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
1754        let [dest, source, count, size] = *args else { return None };
1755        if let Some(plan) = self.nothing(data, &[dest, source], Some(count)) {
1756            return Some(plan);
1757        }
1758        if self.unknown(size) {
1759            return self.unchecked(data, "strncat", &[3]);
1760        }
1761        let len = self.one(source)?.len() as u128;
1762        (self.number(count)? >= len).then(|| self.unchecked(data, "__strcat_chk", &[2]))?
1763    }
1764
1765    /// `__sprintf_chk` and `__vsprintf_chk`, which are the plain call where what they write is known
1766    /// to fit.
1767    ///
1768    /// What they write is known in two shapes, a format with nothing to convert and `"%s"` of a
1769    /// string the module holds, and the second is only readable in the variadic one because a
1770    /// `va_list` is not something this can look inside.
1771    fn sprintf_chk(&self, data: &InstData, name: &str, args: &[Value]) -> Option<Plan> {
1772        let (&flag, &size, &format) = (args.get(1)?, args.get(2)?, args.get(3)?);
1773        let text = self.one(format);
1774        let len = match (text.as_deref(), args.get(4..)?) {
1775            (Some(text), rest)
1776                if !text.contains(&b'%') && (name == "__vsprintf_chk" || rest.is_empty()) =>
1777            {
1778                Some(text.len() as u128)
1779            }
1780            (Some(b"%s"), &[arg]) if name == "__sprintf_chk" => {
1781                self.one(arg).map(|arg| arg.len() as u128)
1782            }
1783            _ => None,
1784        };
1785        let fits =
1786            self.unknown(size) || len.zip(self.number(size)).is_some_and(|(len, size)| len < size);
1787        (fits && self.flagless(flag, text.as_deref()))
1788            .then(|| self.unchecked(data, plain(name), &[1, 2]))?
1789    }
1790
1791    /// `__snprintf_chk` and `__vsnprintf_chk`, which write no more than they were told to and are
1792    /// the plain call where that many fit.
1793    fn snprintf_chk(&self, data: &InstData, name: &str, args: &[Value]) -> Option<Plan> {
1794        let (&count, &flag, &size, &format) =
1795            (args.get(1)?, args.get(2)?, args.get(3)?, args.get(4)?);
1796        let text = self.one(format);
1797        (self.fits(count, size) && self.flagless(flag, text.as_deref()))
1798            .then(|| self.unchecked(data, plain(name), &[2, 3]))?
1799    }
1800
1801    /// Whether the flag a checking printf was handed asks for nothing the plain one does not do.
1802    ///
1803    /// The flag is set above `_FORTIFY_SOURCE=1` and what it adds is refusing `%n` in a format
1804    /// that is writable memory, so a format with nothing to convert, or with a `%s` alone, is one
1805    /// the flag has nothing to say about.
1806    fn flagless(&self, flag: Value, text: Option<&[u8]>) -> bool {
1807        self.number(flag) == Some(0)
1808            || text.is_some_and(|text| !text.contains(&b'%') || text == b"%s")
1809    }
1810
1811    /// The same call to the function that name is, with the arguments at those places left out,
1812    /// where the module allows a call to it that looks like that.
1813    fn unchecked(&self, data: &InstData, name: &str, drop: &'static [usize]) -> Option<Plan> {
1814        let Extra::Call(at) = data.extra else { return None };
1815        let want = without(&self.func[self.func[at].signature], drop)?;
1816        let callee = self.shapes.unchecked(name, &want)?;
1817        Some(Plan::Unchecked { callee, drop })
1818    }
1819
1820    /// Whether a checking call's size is the one that says nothing is known about the object.
1821    ///
1822    /// `__builtin_object_size` answers all ones where it cannot tell, and a check against that is
1823    /// a check nothing can fail.
1824    fn unknown(&self, size: Value) -> bool {
1825        crate::fold::evaluated(self.func, size, DEPTH).is_some_and(|(imm, ty)| imm.signed(ty) == -1)
1826    }
1827
1828    /// Whether a count is known to be no more than the size of the object it is a count of.
1829    fn fits(&self, count: Value, size: Value) -> bool {
1830        self.unknown(size)
1831            || self.largest(count).zip(self.number(size)).is_some_and(|(count, size)| count <= size)
1832    }
1833
1834    /// The largest number this value may work out to, read as an unsigned one.
1835    ///
1836    /// The same walk [`Self::strings`] makes, through block parameters and selects, so
1837    /// `l1 ? sizeof (buf) : 4` is a count of at most the size of `buf` whichever arm was taken. A
1838    /// parameter the walk reaches again is a count a loop kept, as `l` is in
1839    /// `builtins/mempcpy-chk.c`, and adds nothing, for the reason it adds no string.
1840    fn largest(&self, value: Value) -> Option<u128> {
1841        self.largest_on(value, CHAIN, &mut Vec::new())
1842    }
1843
1844    /// The same, with the block parameters whose largest is being worked out.
1845    fn largest_on(&self, value: Value, depth: u32, on: &mut Vec<Value>) -> Option<u128> {
1846        if depth == 0 {
1847            return None;
1848        }
1849        match self.func[value].def {
1850            Def::Param { block, index } => {
1851                if on.contains(&value) {
1852                    return Some(0);
1853                }
1854                let preds = self.cfg.predecessors(block);
1855                on.push(value);
1856                let mut most = None;
1857                for &pred in preds {
1858                    let term = self.func.terminator(pred)?;
1859                    for call in self.func.successors(term).collect::<Vec<_>>() {
1860                        if call.block != block {
1861                            continue;
1862                        }
1863                        let arg = *self.func[call.args].get(index as usize)?;
1864                        most = most.max(Some(self.largest_on(arg, depth - 1, on)?));
1865                    }
1866                }
1867                on.pop();
1868                most
1869            }
1870            Def::Result { inst, .. } if self.func[inst].opcode == Opcode::Select => {
1871                let args = &self.func[self.func[inst].args];
1872                let (then, other) = (*args.get(1)?, *args.get(2)?);
1873                let then = self.largest_on(then, depth - 1, on)?;
1874                Some(then.max(self.largest_on(other, depth - 1, on)?))
1875            }
1876            _ => self.number(value).or_else(|| self.bounded(value, depth, on)),
1877        }
1878    }
1879
1880    /// The largest the arithmetic that made this value says it can be. A mask, a remainder by a
1881    /// constant and a widening of either are what an index worked out to stay inside an array
1882    /// looks like, as `x++ & 7` is in `builtins/strlen.c`, and nothing else is looked at.
1883    fn bounded(&self, value: Value, depth: u32, on: &mut Vec<Value>) -> Option<u128> {
1884        let Def::Result { inst, .. } = self.func[value].def else { return None };
1885        let args = &self.func[self.func[inst].args];
1886        let narrow = *args.first()?;
1887        match self.func[inst].opcode {
1888            Opcode::And => self.number(narrow).or_else(|| self.number(*args.get(1)?)),
1889            Opcode::URem => self.number(*args.get(1)?)?.checked_sub(1),
1890            Opcode::ZExt => self.largest_on(narrow, depth - 1, on),
1891            // A widening that copies the sign keeps the bound only where the sign bit cannot be
1892            // set, which is a bound below the top bit of the narrower type.
1893            Opcode::SExt => {
1894                let most = self.largest_on(narrow, depth - 1, on)?;
1895                let top = 1u128.checked_shl(self.func[narrow].ty.bits().checked_sub(1)?)?;
1896                (most < top).then_some(most)
1897            }
1898            _ => None,
1899        }
1900    }
1901
1902    /// The one length every string this value may point at has, as `foo` has in
1903    /// `builtins/strlen-3.c` after a loop that picks one of four strings of thirteen characters.
1904    fn length(&self, value: Value) -> Option<usize> {
1905        let texts = self.strings(value)?;
1906        let len = texts.first()?.len();
1907        texts.iter().all(|text| text.len() == len).then_some(len)
1908    }
1909
1910    /// The number this value works out to, read as an unsigned one.
1911    fn number(&self, value: Value) -> Option<u128> {
1912        crate::fold::evaluated(self.func, value, DEPTH).map(|(imm, _)| imm.unsigned())
1913    }
1914
1915    /// The length of the longest string this value may point at.
1916    fn longest(&self, value: Value) -> Option<u128> {
1917        self.strings(value)?.iter().map(|text| text.len() as u128).max()
1918    }
1919
1920    /// The `float` spelling of a rounding to a whole number, where the `double` it was handed is a
1921    /// `float` widened and its answer is a `double`, which is the fold gcc makes too.
1922    ///
1923    /// Only the roundings, since `sin ((double) f)` in `float` is a different number from the one
1924    /// in `double` once it is widened back, and only from `float` to `double`, since a `long
1925    /// double` is not one format on every target.
1926    fn narrow(&self, data: &InstData, args: &[Value], callee: &'static str) -> Option<Plan> {
1927        let &[wide] = args else { return None };
1928        let double = Type::float(Float::F64);
1929        let float = Type::float(Float::F32);
1930        if self.func[wide].ty != double || self.answers_float(data) != Some(double) {
1931            return None;
1932        }
1933        let Def::Result { inst, .. } = self.func[wide].def else { return None };
1934        let widened = &self.func[inst];
1935        let &[arg] = &self.func[widened.args] else { return None };
1936        if widened.opcode != Opcode::FPExt || self.func[arg].ty != float {
1937            return None;
1938        }
1939        let (callee, signature) = self.shapes.get(callee)?;
1940        Some(Plan::Narrow { callee, signature, arg })
1941    }
1942
1943    /// The type this call's one result has, where it has one and it is a floating point number.
1944    fn answers_float(&self, data: &InstData) -> Option<Type> {
1945        let mut results = data.results();
1946        let ty = self.func[results.next()?].ty;
1947        (results.next().is_none() && ty.is_float() && !ty.is_vector()).then_some(ty)
1948    }
1949
1950    /// A call to that name, or nothing where this module does not allow one.
1951    fn call(&self, callee: &'static str, args: Vec<Argument>) -> Option<Plan> {
1952        let (callee, signature) = self.shapes.get(callee)?;
1953        Some(Plan::Swap { callee, signature, args, answer: None })
1954    }
1955
1956    /// The one string this value points at, or `None` where there is more than one of them.
1957    fn one(&self, value: Value) -> Option<Vec<u8>> {
1958        let mut candidates = self.strings(value)?;
1959        (candidates.len() == 1).then(|| candidates.pop()).flatten()
1960    }
1961
1962    /// Every string this value may point at, or `None` where any of them is not one this module
1963    /// holds.
1964    ///
1965    /// A block parameter is every argument every branch to that block passes, which is how the
1966    /// conditional expression in `builtins/fputs.c` gets a length without anything having turned it
1967    /// into a `select` first.
1968    fn strings(&self, value: Value) -> Option<Vec<Vec<u8>>> {
1969        self.strings_on(value, CHAIN, &mut Vec::new())
1970    }
1971
1972    /// The same, with the block parameters whose strings are being worked out.
1973    ///
1974    /// A loop that picks a string on some trips and keeps the one it had on the others is a
1975    /// parameter that is one of its own arguments, as `l` is in `builtins/stpcpy-chk.c`. Reached
1976    /// again, it can only be a string one of its other arguments already gave it, so it adds
1977    /// nothing to the list. `depth` still bounds how long a chain is followed.
1978    fn strings_on(&self, value: Value, depth: u32, on: &mut Vec<Value>) -> Option<Vec<Vec<u8>>> {
1979        if depth == 0 {
1980            return None;
1981        }
1982        match self.func[value].def {
1983            Def::Param { block, index } => {
1984                if on.contains(&value) {
1985                    return Some(Vec::new());
1986                }
1987                let preds = self.cfg.predecessors(block);
1988                if preds.is_empty() {
1989                    return None;
1990                }
1991                on.push(value);
1992                let mut all = Vec::new();
1993                for &pred in preds {
1994                    let term = self.func.terminator(pred)?;
1995                    for call in self.func.successors(term).collect::<Vec<_>>() {
1996                        if call.block != block {
1997                            continue;
1998                        }
1999                        let arg = *self.func[call.args].get(index as usize)?;
2000                        all.extend(self.strings_on(arg, depth - 1, on)?);
2001                    }
2002                }
2003                on.pop();
2004                (!all.is_empty()).then_some(all)
2005            }
2006            Def::Result { inst, .. } if self.func[inst].opcode == Opcode::Select => {
2007                let args = &self.func[self.func[inst].args];
2008                let (then, other) = (*args.get(1)?, *args.get(2)?);
2009                let mut all = self.strings_on(then, depth - 1, on)?;
2010                all.extend(self.strings_on(other, depth - 1, on)?);
2011                Some(all)
2012            }
2013            _ => Some(vec![self.literal(value)?]),
2014        }
2015    }
2016
2017    /// The bytes up to the first terminator at the address this value is, where that address is
2018    /// inside a read only object this module vouches for.
2019    fn literal(&self, value: Value) -> Option<Vec<u8>> {
2020        let bytes = self.raw(value)?;
2021        let end = bytes.iter().position(|&byte| byte == 0)?;
2022        Some(bytes[..end].to_vec())
2023    }
2024
2025    /// Every byte from the address this value is to the end of the object it is in.
2026    ///
2027    /// The same walk as above with nothing stopping it at a terminator, because `memchr` is told
2028    /// how far to read rather than going looking for one, and `strnlen` may be told to stop before
2029    /// there is one. A caller that wants a string wants [`Self::literal`] instead.
2030    fn raw(&self, value: Value) -> Option<Vec<u8>> {
2031        let (base, offset) = self.address(value)?;
2032        let Def::Result { inst, .. } = self.func[base].def else { return None };
2033        if self.func[inst].opcode != Opcode::GlobalAddr {
2034            return None;
2035        }
2036        let Extra::Symbol(name) = self.func[inst].extra else { return None };
2037        let Some(SymbolRef::Global(id)) = self.module.lookup(name) else { return None };
2038        let global = &self.module[id];
2039        if !global.constant || !vouched(global, self.pic) {
2040            return None;
2041        }
2042        let mut bytes = Vec::new();
2043        for &datum in &self.module[global.init?] {
2044            match datum {
2045                Datum::Bytes(range) => bytes.extend_from_slice(&self.module[range]),
2046                Datum::Zero(count) => {
2047                    bytes.resize(bytes.len().checked_add(usize::try_from(count).ok()?)?, 0);
2048                }
2049                // A number written in the target's byte order, or an address the linker has not
2050                // filled in. Neither is a byte this can read, and what follows one is at an offset
2051                // that is right only if this one's width is, so the walk stops.
2052                Datum::Scalar { .. } | Datum::Addr(_) | Datum::Away(_) | Datum::Apart { .. } => {
2053                    return None;
2054                }
2055            }
2056        }
2057        // An object whose image stops short of its size is zero from there on, which is what an
2058        // array with fewer initializers than members is and is a byte a search may reach.
2059        let size = usize::try_from(global.size).ok()?;
2060        if bytes.len() < size {
2061            bytes.resize(size, 0);
2062        }
2063        Some(bytes.get(usize::try_from(offset).ok()?..)?.to_vec())
2064    }
2065
2066    /// The address this value is, as something it was computed from and a distance in bytes from
2067    /// it.
2068    ///
2069    /// The same walk [`crate::image`] does down a chain of `ptr_add` of a constant, because an
2070    /// index into a string literal is one of these and the frontend writes one per index.
2071    fn address(&self, mut value: Value) -> Option<(Value, i128)> {
2072        let mut offset: i128 = 0;
2073        for _ in 0..DEPTH {
2074            let Def::Result { inst, .. } = self.func[value].def else {
2075                return Some((value, offset));
2076            };
2077            if self.func[inst].opcode != Opcode::PtrAdd {
2078                return Some((value, offset));
2079            }
2080            let args = &self.func[self.func[inst].args];
2081            offset = offset.checked_add(self.step(*args.get(1)?)?)?;
2082            value = *args.first()?;
2083        }
2084        None
2085    }
2086
2087    /// The distance in bytes this value is, where it works out to a constant.
2088    ///
2089    /// An index into an array is an `int` where the source wrote one, and a pointer is sixty four
2090    /// bits, so what the frontend leaves in front of a `ptr_add` is a `sext` of a constant rather
2091    /// than a constant, and an index the source worked out, as in `s + (x & 3)` with `x` known, is
2092    /// still the arithmetic rather than its answer. This runs before anything has folded either,
2093    /// since everything that would is one function at a time and the function pipeline has not
2094    /// started, so the walk above looks underneath both with [`crate::fold::evaluated`], which is
2095    /// the same arithmetic that pass would do later.
2096    fn step(&self, value: Value) -> Option<i128> {
2097        let (imm, ty) = crate::fold::evaluated(self.func, value, DEPTH)?;
2098        Some(imm.signed(ty))
2099    }
2100}
2101
2102/// Writes one plan into the function.
2103fn apply(
2104    module: &mut Module,
2105    id: FuncId,
2106    names: &mut Interner,
2107    texts: &mut HashMap<Vec<u8>, Symbol>,
2108    inst: Inst,
2109    plan: Plan,
2110) -> HashMap<Value, Value> {
2111    let (callee, signature, args, answer) = match plan {
2112        Plan::Drop => {
2113            module[id].remove_inst(inst);
2114            return HashMap::new();
2115        }
2116        Plan::Answer(answer) => {
2117            let width = size(module);
2118            return answered(&mut module[id], inst, answer, width);
2119        }
2120        Plan::Swap { callee, signature, args, answer } => (callee, signature, args, answer),
2121        Plan::Narrow { callee, signature, arg } => {
2122            let func = &mut module[id];
2123            let Some(old) = func[inst].results().next() else { return HashMap::new() };
2124            let ty = func[old].ty;
2125            let span = func.span(inst);
2126            let varargs = func.push_abis(&[]);
2127            let made = call(func, inst, callee, signature, varargs, &[arg]);
2128            let narrow = func[made].results().next().expect("a rounding is one value");
2129            let args = func.push_values(&[narrow]);
2130            let data = InstData { args, ..InstData::new(Opcode::FPExt) };
2131            let wide = func.create_inst(data, &[ty], span);
2132            func.insert_before(wide, inst);
2133            let value = func[wide].results().next().expect("a conversion is one value");
2134            let forward = HashMap::from([(old, value)]);
2135            uses::substitute(func, &forward);
2136            func.remove_inst(inst);
2137            return forward;
2138        }
2139        Plan::Unchecked { callee, drop } => {
2140            let func = &mut module[id];
2141            let Extra::Call(at) = func[inst].extra else { return HashMap::new() };
2142            let info = func[at];
2143            let Some(signature) = without(&func[info.signature], drop) else {
2144                return HashMap::new();
2145            };
2146            let values: Vec<Value> = func[func[inst].args]
2147                .iter()
2148                .enumerate()
2149                .filter(|(index, _)| !drop.contains(index))
2150                .map(|(_, &value)| value)
2151                .collect();
2152            let made = call(func, inst, callee, signature, info.varargs, &values);
2153            return forward(func, inst, made);
2154        }
2155    };
2156    // The objects first, because a string the fold prints belongs to the module and the module is
2157    // what the function is reached through.
2158    let symbols: Vec<Option<Symbol>> = args
2159        .iter()
2160        .map(|arg| match arg {
2161            Argument::Text(bytes) => Some(object(module, names, texts, bytes)),
2162            _ => None,
2163        })
2164        .collect();
2165    let width = size(module);
2166    let func = &mut module[id];
2167    let span = func.span(inst);
2168    let mut values = Vec::with_capacity(args.len());
2169    for (arg, symbol) in args.iter().zip(symbols) {
2170        values.push(match arg {
2171            Argument::Have(value) => *value,
2172            Argument::Char(byte) => constant(func, inst, int(), i128::from(*byte)),
2173            Argument::Count(count) => constant(func, inst, width, i128::from(*count)),
2174            Argument::At(value, by) => {
2175                let step = constant(func, inst, width, i128::from(*by));
2176                let args = func.push_values(&[*value, step]);
2177                let data = InstData { args, ..InstData::new(Opcode::PtrAdd) };
2178                let made = func.create_inst(data, &[Type::PTR], span);
2179                func.insert_before(made, inst);
2180                func[made].results().next().expect("an address is one value")
2181            }
2182            Argument::Text(_) => {
2183                let extra = Extra::Symbol(symbol.expect("a text argument has an object"));
2184                let data = InstData { extra, ..InstData::new(Opcode::GlobalAddr) };
2185                let made = func.create_inst(data, &[Type::PTR], span);
2186                func.insert_before(made, inst);
2187                func[made].results().next().expect("an address is one value")
2188            }
2189        });
2190    }
2191    let varargs = func.push_abis(&[]);
2192    let made = call(func, inst, callee, signature, varargs, &values);
2193    match answer {
2194        Some(answer) => answered(func, inst, answer, width),
2195        None => forward(func, inst, made),
2196    }
2197}
2198
2199/// The function a checking call is where the check is taken off, which is its name without the
2200/// `__` in front and the `_chk` behind.
2201fn plain(name: &str) -> &str {
2202    name.strip_prefix("__").and_then(|rest| rest.strip_suffix("_chk")).unwrap_or(name)
2203}
2204
2205/// A call to that function with those arguments, put in front of the call being replaced.
2206fn call(
2207    func: &mut Func,
2208    before: Inst,
2209    callee: Symbol,
2210    signature: Signature,
2211    varargs: AbiList,
2212    values: &[Value],
2213) -> Inst {
2214    let span = func.span(before);
2215    let results: Vec<Type> = signature.return_types().collect();
2216    let sig = func.add_signature(signature);
2217    let info = func.add_call(CallInfo { callee: Some(callee), signature: sig, varargs });
2218    let args = func.push_values(values);
2219    let data = InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) };
2220    let made = func.create_inst(data, &results, span);
2221    func.insert_before(made, before);
2222    made
2223}
2224
2225/// Hands whoever read the old call's answer the new one's, takes the old call away, and says what
2226/// was renamed.
2227fn forward(func: &mut Func, old: Inst, new: Inst) -> HashMap<Value, Value> {
2228    // Only where the two are the same kind of thing. The printf family is folded only where nothing
2229    // read it, so the map is empty there and this costs a walk over a function that is about to be
2230    // walked anyway.
2231    let forward: HashMap<Value, Value> = func[old]
2232        .results()
2233        .zip(func[new].results().collect::<Vec<Value>>())
2234        .filter(|&(from, to)| func[from].ty == func[to].ty)
2235        .collect();
2236    if !forward.is_empty() {
2237        uses::substitute(func, &forward);
2238    }
2239    func.remove_inst(old);
2240    forward
2241}
2242
2243/// That signature with the parameters at those places taken out, or `None` where one of the places
2244/// is not a parameter it names.
2245fn without(signature: &Signature, drop: &[usize]) -> Option<Signature> {
2246    drop.iter().all(|&index| index < signature.params.len()).then_some(())?;
2247    let params = signature
2248        .params
2249        .iter()
2250        .enumerate()
2251        .filter(|(index, _)| !drop.contains(index))
2252        .map(|(_, param)| *param)
2253        .collect();
2254    Some(Signature { params, ..signature.clone() })
2255}
2256
2257/// Writes the answer a search worked out in place of the call that would have worked it out, and
2258/// says what was renamed.
2259fn answered(func: &mut Func, inst: Inst, answer: Answer, width: Type) -> HashMap<Value, Value> {
2260    let span = func.span(inst);
2261    let value = match answer {
2262        // The haystack itself, which is what a search for the empty string finds and what a search
2263        // that found its needle at the front of one finds. No instruction at all for either.
2264        Answer::Along(haystack, 0) => haystack,
2265        Answer::Along(haystack, by) => {
2266            let step = constant(func, inst, width, i128::from(by));
2267            let args = func.push_values(&[haystack, step]);
2268            let data = InstData { args, ..InstData::new(Opcode::PtrAdd) };
2269            let made = func.create_inst(data, &[Type::PTR], span);
2270            func.insert_before(made, inst);
2271            func[made].results().next().expect("an address is one value")
2272        }
2273        Answer::Nowhere => {
2274            let zero = constant(func, inst, width, 0);
2275            let args = func.push_values(&[zero]);
2276            let data = InstData { args, ..InstData::new(Opcode::IntToPtr) };
2277            let made = func.create_inst(data, &[Type::PTR], span);
2278            func.insert_before(made, inst);
2279            func[made].results().next().expect("a null pointer is one value")
2280        }
2281        Answer::Number(number) => {
2282            let ty = func[inst]
2283                .results()
2284                .next()
2285                .map(|result| func[result].ty)
2286                .expect("a call whose answer is a number has one");
2287            constant(func, inst, ty, number)
2288        }
2289        Answer::Least { count, len } => {
2290            let ty = func[count].ty;
2291            let len = constant(func, inst, ty, i128::from(len));
2292            let args = func.push_values(&[count, len]);
2293            let data = InstData {
2294                args,
2295                extra: Extra::IntPred(IntPred::Ult),
2296                ..InstData::new(Opcode::ICmp)
2297            };
2298            let made = func.create_inst(data, &[Type::I1], span);
2299            func.insert_before(made, inst);
2300            let shorter = func[made].results().next().expect("a comparison is one value");
2301            let args = func.push_values(&[shorter, count, len]);
2302            let data = InstData { args, ..InstData::new(Opcode::Select) };
2303            let made = func.create_inst(data, &[ty], span);
2304            func.insert_before(made, inst);
2305            func[made].results().next().expect("a choice is one value")
2306        }
2307        Answer::Less { len, step } => {
2308            let ty = func[inst]
2309                .results()
2310                .next()
2311                .map(|result| func[result].ty)
2312                .expect("a call whose answer is a length has one");
2313            let step = resize(func, inst, step, ty);
2314            let len = constant(func, inst, ty, i128::from(len));
2315            let args = func.push_values(&[len, step]);
2316            let data = InstData { args, ..InstData::new(Opcode::Sub) };
2317            let made = func.create_inst(data, &[ty], span);
2318            func.insert_before(made, inst);
2319            func[made].results().next().expect("a difference is one value")
2320        }
2321        Answer::Byte { of, against, leading } => {
2322            let ty = func[inst]
2323                .results()
2324                .next()
2325                .map(|result| func[result].ty)
2326                .expect("a call whose answer is a byte has one");
2327            let read = read(func, inst, of);
2328            // An `unsigned char` is what the standard says a comparison compares, so the byte goes
2329            // into the wider type without its top bit being read as a sign.
2330            let args = func.push_values(&[read]);
2331            let data = InstData { args, ..InstData::new(Opcode::ZExt) };
2332            let made = func.create_inst(data, &[ty], span);
2333            func.insert_before(made, inst);
2334            let wide = func[made].results().next().expect("a conversion is one value");
2335            let other = constant(func, inst, ty, i128::from(against));
2336            let pair = if leading { [other, wide] } else { [wide, other] };
2337            let args = func.push_values(&pair);
2338            let data = InstData { args, ..InstData::new(Opcode::Sub) };
2339            let made = func.create_inst(data, &[ty], span);
2340            func.insert_before(made, inst);
2341            func[made].results().next().expect("a difference is one value")
2342        }
2343    };
2344    let forward: HashMap<Value, Value> =
2345        func[inst].results().map(|result| (result, value)).collect();
2346    uses::substitute(func, &forward);
2347    func.remove_inst(inst);
2348    forward
2349}
2350
2351/// How two strings compare, over however many bytes the comparison is allowed to read.
2352fn walk(left: &[u8], right: &[u8], bound: usize) -> i128 {
2353    // The terminator is part of the comparison, since it is what stops one string before the other
2354    // and it is smaller than every byte that could be opposite it.
2355    for at in 0..bound.min(left.len() + 1).min(right.len() + 1) {
2356        let (this, that) =
2357            (left.get(at).copied().unwrap_or(0), right.get(at).copied().unwrap_or(0));
2358        if this != that {
2359            return if this < that { -1 } else { 1 };
2360        }
2361        if this == 0 {
2362            break;
2363        }
2364    }
2365    0
2366}
2367
2368/// Where the second string is inside the first, in bytes from its front.
2369fn at(haystack: &[u8], needle: &[u8]) -> Option<usize> {
2370    haystack.windows(needle.len()).position(|window| window == needle)
2371}
2372
2373/// The byte at that address, read in front of the call being replaced.
2374///
2375/// A comparison reads the first byte of both of its strings before it can answer anything, so this
2376/// read is one the call was going to make and is safe wherever the call itself was.
2377fn read(func: &mut Func, before: Inst, from: Value) -> Value {
2378    let span = func.span(before);
2379    let mem = func.add_mem(MemInfo {
2380        size: 1,
2381        align: 1,
2382        order: MemOrder::NotAtomic,
2383        tbaa: None,
2384        owns: 0,
2385        restrict: Restrict::NONE,
2386    });
2387    let args = func.push_values(&[from]);
2388    let data = InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) };
2389    let made = func.create_inst(data, &[Type::int(8)], span);
2390    func.insert_before(made, before);
2391    func[made].results().next().expect("a load is one value")
2392}
2393
2394/// That value in an integer type of another width, put in front of the call being replaced. The
2395/// value is known not to be negative, so a wider type takes it without its sign.
2396fn resize(func: &mut Func, before: Inst, value: Value, ty: Type) -> Value {
2397    let opcode = match func[value].ty.bits().cmp(&ty.bits()) {
2398        std::cmp::Ordering::Equal => return value,
2399        std::cmp::Ordering::Less => Opcode::ZExt,
2400        std::cmp::Ordering::Greater => Opcode::Trunc,
2401    };
2402    let span = func.span(before);
2403    let args = func.push_values(&[value]);
2404    let made = func.create_inst(InstData { args, ..InstData::new(opcode) }, &[ty], span);
2405    func.insert_before(made, before);
2406    func[made].results().next().expect("a conversion is one value")
2407}
2408
2409/// An integer constant of that type, put in front of the call being replaced.
2410fn constant(func: &mut Func, before: Inst, ty: Type, value: i128) -> Value {
2411    let span = func.span(before);
2412    let imm = func.add_imm(Imm::int(value, ty.lane()));
2413    let data = InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) };
2414    let made = func.create_inst(data, &[ty], span);
2415    func.insert_before(made, before);
2416    func[made].results().next().expect("a constant is one value")
2417}
2418
2419/// The read only object holding these bytes and a terminator, making it the first time it is asked
2420/// for.
2421///
2422/// `.Lfold` rather than `.Lstr`, so that this numbering and the frontend's cannot meet, and a
2423/// number past the end of the table in the case where a program has named one of these itself.
2424fn object(
2425    module: &mut Module,
2426    names: &mut Interner,
2427    texts: &mut HashMap<Vec<u8>, Symbol>,
2428    bytes: &[u8],
2429) -> Symbol {
2430    if let Some(&symbol) = texts.get(bytes) {
2431        return symbol;
2432    }
2433    let mut image = bytes.to_vec();
2434    image.push(0);
2435    let mut symbol = names.intern(&format!(".Lfold.{}", texts.len()));
2436    for next in texts.len().. {
2437        if module.lookup(symbol).is_none() {
2438            break;
2439        }
2440        symbol = names.intern(&format!(".Lfold.{}", next + 1));
2441    }
2442    let mut global = Global::new(symbol, image.len() as u64, 1);
2443    global.linkage = Linkage::Internal;
2444    global.constant = true;
2445    let range = module.push_bytes(&image);
2446    global.init = Some(module.push_data(&[Datum::Bytes(range)]));
2447    module.add_global(global);
2448    texts.insert(bytes.to_vec(), symbol);
2449    symbol
2450}
2451
2452#[cfg(test)]
2453mod tests {
2454    use super::*;
2455
2456    /// What every fixture below starts with, which is the target the counts and widths are of.
2457    const HEAD: &str = "\
2458; ModuleID = 't.c'
2459; format 0
2460target triple = \"x86_64-unknown-linux-gnu\"
2461target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
2462";
2463
2464    /// The module that text is, folded, printed, and checked by the verifier on the way out.
2465    ///
2466    /// Printing it rather than walking it, because what a reader of one of these tests wants to
2467    /// know is what the function ended up being, and a chain of accessors says that less clearly
2468    /// than the line it produces.
2469    fn folded(body: &str) -> String {
2470        run(body, &[], &mut Fuel::unlimited())
2471    }
2472
2473    /// The same, under whatever `-fno-builtin-<name>` and fuel the test wants.
2474    fn run(body: &str, no_builtin: &[String], fuel: &mut Fuel) -> String {
2475        let mut names = Interner::new();
2476        let text = format!("{HEAD}{body}");
2477        let mut module = rucc_ir::parse(&text, &mut names).expect("the fixture parses");
2478        fold(&mut module, &mut names, no_builtin, Pic::Executable, fuel);
2479        if let Err(errors) = rucc_ir::verify(&module, &names) {
2480            panic!("the fold left invalid IR, {errors:?}\n{}", rucc_ir::print(&module, &names));
2481        }
2482        rucc_ir::print(&module, &names)
2483    }
2484
2485    /// A `printf` of a format holding no `%` and ending in a newline is a `puts` of the rest of it.
2486    ///
2487    /// The rest of it is a string this module did not hold, because "hello world" terminated is not
2488    /// a suffix of "hello world\n" terminated, so the fold has to leave an object behind as well as
2489    /// a call.
2490    #[test]
2491    fn a_format_that_ends_in_a_newline_is_written_by_puts() {
2492        let out = folded(
2493            r#"
2494global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
2495
2496func @printf(ptr, ...) -> i32, linkage(external);
2497
2498func @g(), linkage(external) {
2499block0:
2500    %0 = global_addr @.Lstr.0
2501    %1 = call @printf(%0) : (ptr, ...) -> i32
2502    return
2503}
2504"#,
2505        );
2506        assert!(out.contains("call @puts("), "{out}");
2507        assert!(!out.contains("call @printf("), "{out}");
2508        assert!(out.contains(r#"@.Lfold.0 : bytes 12 = { bytes "hello world\00" }"#), "{out}");
2509    }
2510
2511    /// A one character format is a `putchar` of that character, and an empty one is nothing at all.
2512    #[test]
2513    fn a_short_format_is_written_by_putchar_or_by_nothing() {
2514        let out = folded(
2515            r#"
2516global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
2517global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2518
2519func @printf(ptr, ...) -> i32, linkage(external);
2520
2521func @g(), linkage(external) {
2522block0:
2523    %0 = global_addr @.Lstr.0
2524    %1 = call @printf(%0) : (ptr, ...) -> i32
2525    %2 = global_addr @.Lstr.1
2526    %3 = call @printf(%2) : (ptr, ...) -> i32
2527    return
2528}
2529"#,
2530        );
2531        assert!(out.contains("iconst.i32 120"), "the character is the argument, {out}");
2532        assert!(out.contains("call @putchar("), "{out}");
2533        assert_eq!(out.matches("call @").count(), 1, "the empty one is gone, {out}");
2534    }
2535
2536    /// `printf("%s\n", p)` is `puts(p)` and `printf("%c", c)` is `putchar(c)`, whatever the
2537    /// argument is.
2538    #[test]
2539    fn the_two_formats_that_are_a_call_on_their_own_are_folded_for_any_argument() {
2540        let out = folded(
2541            r#"
2542global @.Lstr.0 : bytes 4 = { bytes "%s\0a\00" }, align 1, linkage(internal), constant
2543global @.Lstr.1 : bytes 3 = { bytes "%c\00" }, align 1, linkage(internal), constant
2544
2545func @printf(ptr, ...) -> i32, linkage(external);
2546
2547func @g(ptr, i32), linkage(external) {
2548block0(%0: ptr, %1: i32):
2549    %2 = global_addr @.Lstr.0
2550    %3 = call @printf(%2, %0) : (ptr, ...) -> i32
2551    %4 = global_addr @.Lstr.1
2552    %5 = call @printf(%4, %1) : (ptr, ...) -> i32
2553    return
2554}
2555"#,
2556        );
2557        assert!(out.contains("call @puts(%0)"), "{out}");
2558        assert!(out.contains("call @putchar(%1)"), "{out}");
2559    }
2560
2561    /// `printf("%s", p)` where `p` is not a string this module holds stays as it is.
2562    ///
2563    /// There is no stream argument to hand to `fputs`, and `stdout` is not a name a compiler may
2564    /// invent. gcc stops in the same place.
2565    #[test]
2566    fn a_string_argument_nothing_is_known_about_is_left_to_printf() {
2567        let out = folded(
2568            r#"
2569global @.Lstr.0 : bytes 3 = { bytes "%s\00" }, align 1, linkage(internal), constant
2570
2571func @printf(ptr, ...) -> i32, linkage(external);
2572
2573func @g(ptr), linkage(external) {
2574block0(%0: ptr):
2575    %1 = global_addr @.Lstr.0
2576    %2 = call @printf(%1, %0) : (ptr, ...) -> i32
2577    return
2578}
2579"#,
2580        );
2581        assert!(out.contains("call @printf("), "{out}");
2582    }
2583
2584    /// The `fprintf` list, which is the `printf` one with a stream in hand.
2585    ///
2586    /// A format holding no `%` is an `fwrite` of the whole of it rather than a `puts` of part of
2587    /// it, since `fwrite` is told how many bytes to write and adds no newline of its own.
2588    #[test]
2589    fn a_stream_takes_the_whole_format_through_fwrite() {
2590        let out = folded(
2591            r#"
2592global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
2593global @.Lstr.1 : bytes 2 = { bytes "q\00" }, align 1, linkage(internal), constant
2594
2595func @fprintf(ptr, ptr, ...) -> i32, linkage(external);
2596
2597func @g(ptr), linkage(external) {
2598block0(%0: ptr):
2599    %1 = global_addr @.Lstr.0
2600    %2 = call @fprintf(%0, %1) : (ptr, ptr, ...) -> i32
2601    %3 = global_addr @.Lstr.1
2602    %4 = call @fprintf(%0, %3) : (ptr, ptr, ...) -> i32
2603    return
2604}
2605"#,
2606        );
2607        assert!(out.contains("call @fwrite("), "{out}");
2608        assert!(out.contains("iconst.i64 12"), "the whole format, newline and all, {out}");
2609        assert!(out.contains("call @fputc("), "{out}");
2610        assert!(!out.contains("call @fprintf("), "{out}");
2611    }
2612
2613    /// `fprintf(s, "%s", p)` is `fputs(p, s)` however little is known about `p`, which is the fold
2614    /// `printf` cannot have.
2615    #[test]
2616    fn a_string_argument_with_a_stream_beside_it_becomes_fputs() {
2617        let out = folded(
2618            r#"
2619global @.Lstr.0 : bytes 3 = { bytes "%s\00" }, align 1, linkage(internal), constant
2620
2621func @fprintf(ptr, ptr, ...) -> i32, linkage(external);
2622
2623func @g(ptr, ptr), linkage(external) {
2624block0(%0: ptr, %1: ptr):
2625    %2 = global_addr @.Lstr.0
2626    %3 = call @fprintf(%0, %2, %1) : (ptr, ptr, ...) -> i32
2627    return
2628}
2629"#,
2630        );
2631        assert!(out.contains("call @fputs(%1, %0)"), "{out}");
2632    }
2633
2634    /// An `fputs` of a string whose length is known is an `fwrite` of that many bytes, one of a
2635    /// single character is an `fputc`, and one of nothing is nothing.
2636    #[test]
2637    fn fputs_of_a_string_this_module_holds_is_folded_by_its_length() {
2638        let out = folded(
2639            r#"
2640global @.Lstr.0 : bytes 7 = { bytes "abcdef\00" }, align 1, linkage(internal), constant
2641global @.Lstr.1 : bytes 2 = { bytes "z\00" }, align 1, linkage(internal), constant
2642global @.Lstr.2 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2643
2644func @fputs(ptr, ptr) -> i32, linkage(external);
2645
2646func @g(ptr), linkage(external) {
2647block0(%0: ptr):
2648    %1 = global_addr @.Lstr.0
2649    %2 = call @fputs(%1, %0) : (ptr, ptr) -> i32
2650    %3 = global_addr @.Lstr.1
2651    %4 = call @fputs(%3, %0) : (ptr, ptr) -> i32
2652    %5 = global_addr @.Lstr.2
2653    %6 = call @fputs(%5, %0) : (ptr, ptr) -> i32
2654    return
2655}
2656"#,
2657        );
2658        assert!(out.contains("call @fwrite("), "{out}");
2659        assert!(out.contains("iconst.i64 6"), "{out}");
2660        assert!(out.contains("iconst.i32 122"), "{out}");
2661        assert!(out.contains("call @fputc("), "{out}");
2662        assert!(!out.contains("call @fputs("), "the empty one is gone too, {out}");
2663    }
2664
2665    /// An index into a string literal is a string as well, which is what `fputs(s1 + 6, s)` is.
2666    ///
2667    /// The index is an `int` widened to the width of a pointer, because that is what the frontend
2668    /// writes for an index the source wrote as one and nothing has folded it yet where this runs.
2669    /// An index landing on the terminator is the empty string, so that call goes altogether.
2670    #[test]
2671    fn an_index_into_a_literal_is_a_string_of_its_own() {
2672        let out = folded(
2673            r#"
2674global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2675
2676func @fputs(ptr, ptr) -> i32, linkage(external);
2677
2678func @g(ptr), linkage(external) {
2679block0(%0: ptr):
2680    %1 = global_addr @.Lstr.0
2681    %2 = iconst.i32 6
2682    %3 = sext.i64 %2
2683    %4 = ptr_add %1, %3
2684    %5 = call @fputs(%4, %0) : (ptr, ptr) -> i32
2685    %6 = iconst.i32 11
2686    %7 = sext.i64 %6
2687    %8 = ptr_add %1, %7
2688    %9 = call @fputs(%8, %0) : (ptr, ptr) -> i32
2689    return
2690}
2691"#,
2692        );
2693        assert!(out.contains("iconst.i64 5"), "world without its terminator, {out}");
2694        assert!(out.contains("call @fwrite("), "{out}");
2695        assert!(!out.contains("call @fputs("), "and the terminator itself is nothing, {out}");
2696    }
2697
2698    /// A conditional expression whose arms are two literals of one length is folded, and one whose
2699    /// arms are two lengths is not.
2700    ///
2701    /// The arms reach the call through a block parameter rather than through a `select`, because
2702    /// that is what the lowering walk builds for a conditional expression, so the walk that answers
2703    /// what string a value is has to go up through the branches to find them.
2704    #[test]
2705    fn a_choice_between_two_literals_is_folded_when_they_are_the_same_length() {
2706        let text = r#"
2707global @.Lstr.0 : bytes 2 = { bytes "f\00" }, align 1, linkage(internal), constant
2708global @.Lstr.1 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
2709global @.Lstr.2 : bytes 4 = { bytes "abc\00" }, align 1, linkage(internal), constant
2710
2711func @fputs(ptr, ptr) -> i32, linkage(external);
2712
2713func @g(ptr, i1), linkage(external) {
2714block0(%0: ptr, %1: i1):
2715    %2 = global_addr @.LEFT
2716    %3 = global_addr @.Lstr.1
2717    br_if %1, block1(%2), block1(%3)
2718block1(%4: ptr):
2719    %5 = call @fputs(%4, %0) : (ptr, ptr) -> i32
2720    return
2721}
2722"#;
2723        let same = folded(&text.replace(".LEFT", ".Lstr.0"));
2724        assert!(same.contains("call @fwrite("), "{same}");
2725        assert!(same.contains("iconst.i64 1"), "{same}");
2726
2727        let differing = folded(&text.replace(".LEFT", ".Lstr.2"));
2728        assert!(differing.contains("call @fputs("), "{differing}");
2729    }
2730
2731    /// A call whose result something reads is left alone.
2732    ///
2733    /// `printf` answers how many characters it wrote and `puts` answers a number that is not that
2734    /// count, so a program looking at the answer is a program this may not touch.
2735    #[test]
2736    fn a_call_whose_answer_is_read_is_not_folded() {
2737        let out = folded(
2738            r#"
2739global @n : bytes 4 = { zero 4 }, align 4, linkage(external)
2740global @.Lstr.0 : bytes 3 = { bytes "a\0a\00" }, align 1, linkage(internal), constant
2741
2742func @printf(ptr, ...) -> i32, linkage(external);
2743
2744func @g(), linkage(external) {
2745block0:
2746    %0 = global_addr @.Lstr.0
2747    %1 = call @printf(%0) : (ptr, ...) -> i32
2748    %2 = global_addr @n
2749    store %1 -> %2, align 4
2750    return
2751}
2752"#,
2753        );
2754        assert!(out.contains("call @printf("), "{out}");
2755    }
2756
2757    /// A body the program wrote for a standard name does not stop a call to that name being folded,
2758    /// which is what gcc 16 does and what `execute/vprintf-chk-1.c` checks by defining the checking
2759    /// function above the calls it expects to be folded away. The body itself is left alone, so a
2760    /// `puts` of the program's own that prints with `printf` does not become a call to itself.
2761    #[test]
2762    fn a_body_for_a_standard_name_is_not_folded_inside_and_does_not_stop_the_fold() {
2763        let out = folded(
2764            r#"
2765global @.Lstr.0 : bytes 3 = { bytes "a\0a\00" }, align 1, linkage(internal), constant
2766
2767func @printf(ptr, ...) -> i32, linkage(external);
2768
2769func @puts(ptr) -> i32, linkage(external) {
2770block0(%0: ptr):
2771    %1 = global_addr @.Lstr.0
2772    %2 = call @printf(%1) : (ptr, ...) -> i32
2773    %3 = iconst.i32 0
2774    return %3
2775}
2776
2777func @__vprintf_chk(i32, ptr, ptr) -> i32, linkage(external) {
2778block0(%0: i32, %1: ptr, %2: ptr):
2779    %3 = iconst.i32 0
2780    return %3
2781}
2782
2783func @g(ptr), linkage(external) {
2784block0(%0: ptr):
2785    %1 = iconst.i32 1
2786    %2 = global_addr @.Lstr.0
2787    %3 = call @__vprintf_chk(%1, %2, %0) : (i32, ptr, ptr) -> i32
2788    return
2789}
2790"#,
2791        );
2792        assert!(!out.contains("call @__vprintf_chk("), "{out}");
2793        assert_eq!(out.matches("call @puts(").count(), 1, "{out}");
2794        assert!(out.contains("call @printf("), "the body of `puts` keeps its own call, {out}");
2795    }
2796
2797    /// A program that declared `puts` as something else keeps the call it had.
2798    ///
2799    /// The verifier holds that a call to a name this module declares carries that name's signature,
2800    /// so the fold either agrees with the declaration or does not happen. A fold that went ahead
2801    /// here would produce IR the compiler itself refuses.
2802    #[test]
2803    fn a_declaration_of_another_shape_stops_the_fold() {
2804        let out = folded(
2805            r#"
2806global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
2807
2808func @printf(ptr, ...) -> i32, linkage(external);
2809func @puts(ptr, i32) -> i32, linkage(external);
2810
2811func @g(), linkage(external) {
2812block0:
2813    %0 = global_addr @.Lstr.0
2814    %1 = call @printf(%0) : (ptr, ...) -> i32
2815    return
2816}
2817"#,
2818        );
2819        assert!(out.contains("call @printf("), "{out}");
2820        assert!(!out.contains("@.Lfold."), "and no object was left behind either, {out}");
2821    }
2822
2823    /// A variable by one of those names stops it as well, since a variable is not a function to
2824    /// call.
2825    #[test]
2826    fn a_variable_by_the_name_of_a_replacement_stops_the_fold() {
2827        let out = folded(
2828            r#"
2829global @putchar : bytes 4 = { zero 4 }, align 4, linkage(external)
2830global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
2831
2832func @printf(ptr, ...) -> i32, linkage(external);
2833
2834func @g(), linkage(external) {
2835block0:
2836    %0 = global_addr @.Lstr.0
2837    %1 = call @printf(%0) : (ptr, ...) -> i32
2838    return
2839}
2840"#,
2841        );
2842        assert!(out.contains("call @printf("), "{out}");
2843    }
2844
2845    /// An `_unlocked` spelling gets the one fold that names no function.
2846    ///
2847    /// A system need not have a `puts_unlocked` for the compiler to name, which is the reason the
2848    /// torture program gives and the place gcc stops too.
2849    #[test]
2850    fn the_unlocked_spellings_are_only_removed_when_they_write_nothing() {
2851        let out = folded(
2852            r#"
2853global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
2854global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2855
2856func @printf_unlocked(ptr, ...) -> i32, linkage(external);
2857
2858func @g(), linkage(external) {
2859block0:
2860    %0 = global_addr @.Lstr.0
2861    %1 = call @printf_unlocked(%0) : (ptr, ...) -> i32
2862    %2 = global_addr @.Lstr.1
2863    %3 = call @printf_unlocked(%2) : (ptr, ...) -> i32
2864    return
2865}
2866"#,
2867        );
2868        assert_eq!(out.matches("call @printf_unlocked(").count(), 1, "{out}");
2869        assert!(!out.contains("call @puts("), "{out}");
2870    }
2871
2872    /// `-fno-builtin-printf` takes one name away and leaves the rest of the family folded.
2873    #[test]
2874    fn one_name_can_be_taken_away_without_taking_the_family_away() {
2875        let body = r#"
2876global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
2877
2878func @printf(ptr, ...) -> i32, linkage(external);
2879func @fputs(ptr, ptr) -> i32, linkage(external);
2880
2881func @g(ptr), linkage(external) {
2882block0(%0: ptr):
2883    %1 = global_addr @.Lstr.0
2884    %2 = call @printf(%1) : (ptr, ...) -> i32
2885    %3 = call @fputs(%1, %0) : (ptr, ptr) -> i32
2886    return
2887}
2888"#;
2889        let out = run(body, &["printf".to_owned()], &mut Fuel::unlimited());
2890        assert!(out.contains("call @printf("), "{out}");
2891        assert!(out.contains("call @fputc("), "and the other one still folded, {out}");
2892    }
2893
2894    /// Fuel stops it, which is what a bisection over a miscompilation needs of every transformation
2895    /// here.
2896    #[test]
2897    fn a_run_out_of_fuel_transforms_nothing() {
2898        let body = r#"
2899global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
2900
2901func @printf(ptr, ...) -> i32, linkage(external);
2902
2903func @g(), linkage(external) {
2904block0:
2905    %0 = global_addr @.Lstr.0
2906    %1 = call @printf(%0) : (ptr, ...) -> i32
2907    return
2908}
2909"#;
2910        let mut fuel = Fuel::of(0);
2911        let out = run(body, &[], &mut fuel);
2912        assert!(out.contains("call @printf("), "{out}");
2913        assert_eq!(fuel.spent(), 0);
2914    }
2915
2916    /// Two calls folded to the same string share one object rather than getting one each.
2917    #[test]
2918    fn one_object_serves_every_call_that_prints_the_same_thing() {
2919        let out = folded(
2920            r#"
2921global @.Lstr.0 : bytes 4 = { bytes "hi\0a\00" }, align 1, linkage(internal), constant
2922
2923func @printf(ptr, ...) -> i32, linkage(external);
2924
2925func @g(), linkage(external) {
2926block0:
2927    %0 = global_addr @.Lstr.0
2928    %1 = call @printf(%0) : (ptr, ...) -> i32
2929    %2 = call @printf(%0) : (ptr, ...) -> i32
2930    return
2931}
2932
2933func @h(), linkage(external) {
2934block0:
2935    %0 = global_addr @.Lstr.0
2936    %1 = call @printf(%0) : (ptr, ...) -> i32
2937    return
2938}
2939"#,
2940        );
2941        assert_eq!(out.matches("@.Lfold.0 : bytes").count(), 1, "{out}");
2942        assert!(!out.contains("@.Lfold.1"), "{out}");
2943        assert_eq!(out.matches("call @puts(").count(), 3, "{out}");
2944    }
2945
2946    /// A search for the empty string finds it at the front of whatever it was given.
2947    ///
2948    /// The haystack need not be a string this module holds, because the answer does not depend on
2949    /// what is in it.
2950    #[test]
2951    fn a_search_for_nothing_answers_with_the_haystack_itself() {
2952        let out = folded(
2953            r#"
2954global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2955
2956func @strstr(ptr, ptr) -> ptr, linkage(external);
2957
2958func @g(ptr) -> ptr, linkage(external) {
2959block0(%0: ptr):
2960    %1 = global_addr @.Lstr.0
2961    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
2962    return %2
2963}
2964"#,
2965        );
2966        assert!(!out.contains("call @strstr("), "{out}");
2967        assert!(out.contains("return %0"), "{out}");
2968    }
2969
2970    /// Two strings this module holds answer themselves, at a place in the first or nowhere in it.
2971    #[test]
2972    fn two_strings_this_module_holds_answer_without_a_call() {
2973        let out = folded(
2974            r#"
2975global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2976global @.Lstr.1 : bytes 2 = { bytes "w\00" }, align 1, linkage(internal), constant
2977global @.Lstr.2 : bytes 3 = { bytes "zz\00" }, align 1, linkage(internal), constant
2978
2979func @strstr(ptr, ptr) -> ptr, linkage(external);
2980func @use(ptr, ptr), linkage(external);
2981
2982func @g(), linkage(external) {
2983block0:
2984    %0 = global_addr @.Lstr.0
2985    %1 = global_addr @.Lstr.1
2986    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
2987    %3 = global_addr @.Lstr.2
2988    %4 = call @strstr(%0, %3) : (ptr, ptr) -> ptr
2989    call @use(%2, %4) : (ptr, ptr)
2990    return
2991}
2992"#,
2993        );
2994        assert!(!out.contains("call @strstr("), "{out}");
2995        assert!(out.contains("ptr_add %0, "), "the w is six bytes along, {out}");
2996        assert!(out.contains("iconst.i64 6"), "{out}");
2997        assert!(out.contains("inttoptr"), "and the zz is nowhere in it, {out}");
2998    }
2999
3000    /// A one character needle is a search for a character, which `strchr` is the name of.
3001    #[test]
3002    fn a_one_character_needle_becomes_a_search_for_that_character() {
3003        let out = folded(
3004            r#"
3005global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
3006
3007func @strstr(ptr, ptr) -> ptr, linkage(external);
3008
3009func @g(ptr) -> ptr, linkage(external) {
3010block0(%0: ptr):
3011    %1 = global_addr @.Lstr.0
3012    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
3013    return %2
3014}
3015"#,
3016        );
3017        assert!(!out.contains("call @strstr("), "{out}");
3018        assert!(out.contains("call @strchr(%0, "), "{out}");
3019        assert!(out.contains("iconst.i32 111"), "{out}");
3020    }
3021
3022    /// A module whose `strchr` is something else of that name keeps its `strstr` call.
3023    #[test]
3024    fn a_strchr_of_another_shape_is_not_the_one_to_call() {
3025        let out = folded(
3026            r#"
3027global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
3028
3029func @strstr(ptr, ptr) -> ptr, linkage(external);
3030func @strchr(ptr, ptr) -> ptr, linkage(external);
3031
3032func @g(ptr) -> ptr, linkage(external) {
3033block0(%0: ptr):
3034    %1 = global_addr @.Lstr.0
3035    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
3036    return %2
3037}
3038"#,
3039        );
3040        assert!(out.contains("call @strstr("), "{out}");
3041    }
3042
3043    /// A declaration renamed by an assembler name is the function the standard describes still.
3044    ///
3045    /// The call names the symbol the rename asked for, and the fold reads the spelling beside it,
3046    /// which is what `gcc.c-torture/execute/builtins/strstr-asm.c` is written to catch.
3047    #[test]
3048    fn a_renamed_declaration_is_still_the_function_it_was_spelled() {
3049        let out = folded(
3050            r#"
3051global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3052
3053func @my_strstr(ptr, ptr) -> ptr, linkage(external), spelled "strstr";
3054
3055func @g(ptr) -> ptr, linkage(external) {
3056block0(%0: ptr):
3057    %1 = global_addr @.Lstr.0
3058    %2 = call @my_strstr(%0, %1) : (ptr, ptr) -> ptr
3059    return %2
3060}
3061"#,
3062        );
3063        assert!(!out.contains("call @my_strstr("), "{out}");
3064        assert!(out.contains("return %0"), "{out}");
3065    }
3066
3067    /// A module that renamed `strchr` gets a call to the symbol it renamed it to.
3068    #[test]
3069    fn a_renamed_replacement_is_called_by_the_symbol_the_rename_asked_for() {
3070        let out = folded(
3071            r#"
3072global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
3073
3074func @strstr(ptr, ptr) -> ptr, linkage(external);
3075func @my_strchr(ptr, i32) -> ptr, linkage(external), spelled "strchr";
3076
3077func @g(ptr) -> ptr, linkage(external) {
3078block0(%0: ptr):
3079    %1 = global_addr @.Lstr.0
3080    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
3081    return %2
3082}
3083"#,
3084        );
3085        assert!(out.contains("call @my_strchr(%0, "), "{out}");
3086        assert!(!out.contains("call @strchr("), "{out}");
3087    }
3088
3089    /// `-fno-builtin-strstr` leaves the call alone.
3090    #[test]
3091    fn a_strstr_taken_away_is_a_call_like_any_other() {
3092        let body = r#"
3093global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3094
3095func @strstr(ptr, ptr) -> ptr, linkage(external);
3096
3097func @g(ptr) -> ptr, linkage(external) {
3098block0(%0: ptr):
3099    %1 = global_addr @.Lstr.0
3100    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
3101    return %2
3102}
3103"#;
3104        let out = run(body, &["strstr".to_owned()], &mut Fuel::unlimited());
3105        assert!(out.contains("call @strstr("), "{out}");
3106    }
3107
3108    /// `strlen` of a string this module holds is its length, and of a place inside one is the rest.
3109    #[test]
3110    fn strlen_of_a_string_this_module_holds_is_a_number() {
3111        let out = folded(
3112            r#"
3113global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3114
3115func @strlen(ptr) -> i64, linkage(external);
3116func @use(i64, i64), linkage(external);
3117
3118func @g(), linkage(external) {
3119block0:
3120    %0 = global_addr @.Lstr.0
3121    %1 = call @strlen(%0) : (ptr) -> i64
3122    %2 = iconst.i64 6
3123    %3 = ptr_add %0, %2
3124    %4 = call @strlen(%3) : (ptr) -> i64
3125    call @use(%1, %4) : (i64, i64)
3126    return
3127}
3128"#,
3129        );
3130        assert!(!out.contains("call @strlen("), "{out}");
3131        assert!(out.contains("iconst.i64 11"), "{out}");
3132        assert!(out.contains("iconst.i64 5"), "the world on its own, {out}");
3133    }
3134
3135    /// `memmove` is `memcpy` where the two sides cannot overlap and the destination where it moves
3136    /// nothing, and `bcopy` is the same with its addresses the other way round.
3137    #[test]
3138    fn a_move_that_cannot_overlap_is_a_copy() {
3139        let out = folded(
3140            r#"
3141global @.Lstr.0 : bytes 6 = { bytes "abcde\00" }, align 1, linkage(internal), constant
3142global @p : bytes 32 = { zero 32 }, align 16, linkage(external)
3143
3144func @memmove(ptr, ptr, i64) -> ptr, linkage(external);
3145func @bcopy(ptr, ptr, i64), linkage(external);
3146func @use(ptr, ptr, ptr, ptr), linkage(external);
3147
3148func @g(ptr, i64), linkage(external) {
3149block0(%0: ptr, %1: i64):
3150    %2 = global_addr @p
3151    %3 = global_addr @.Lstr.0
3152    %4 = iconst.i64 6
3153    %5 = call @memmove(%2, %3, %4) : (ptr, ptr, i64) -> ptr
3154    %6 = iconst.i64 2
3155    %7 = ptr_add %2, %6
3156    %8 = iconst.i64 3
3157    %9 = ptr_add %2, %8
3158    %10 = iconst.i64 1
3159    %11 = call @memmove(%7, %9, %10) : (ptr, ptr, i64) -> ptr
3160    %12 = iconst.i64 0
3161    %13 = call @memmove(%7, %0, %12) : (ptr, ptr, i64) -> ptr
3162    call @bcopy(%9, %7, %10) : (ptr, ptr, i64)
3163    %14 = alloca, size 8, align 8
3164    %15 = call @memmove(%14, %0, %1) : (ptr, ptr, i64) -> ptr
3165    %16 = call @memmove(%7, %9, %1) : (ptr, ptr, i64) -> ptr
3166    call @use(%5, %11, %13, %16) : (ptr, ptr, ptr, ptr)
3167    return
3168}
3169"#,
3170        );
3171        assert_eq!(out.matches("call @memcpy(").count(), 3, "{out}");
3172        assert!(!out.contains("call @bcopy("), "{out}");
3173        assert_eq!(
3174            out.matches("call @memmove(").count(),
3175            2,
3176            "a local and a pointer from outside, and two places in one object, stay moves, {out}"
3177        );
3178    }
3179
3180    /// `strcat` onto a local array that `memset` and `strcpy` just filled, from a block in front
3181    /// with only the one way in, is a copy to where the terminator was, and a chain of them folds
3182    /// one a round. A call that may have written the array in between leaves it a call.
3183    #[test]
3184    fn strcat_onto_what_was_just_written_is_a_copy_to_its_end() {
3185        let text = r#"
3186global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3187global @.Lstr.1 : bytes 6 = { bytes " 1111\00" }, align 1, linkage(internal), constant
3188global @.Lstr.2 : bytes 3 = { bytes "ab\00" }, align 1, linkage(internal), constant
3189
3190func @strcat(ptr, ptr) -> ptr, linkage(external);
3191func @strcpy(ptr, ptr) -> ptr, linkage(external);
3192func @memset(ptr, i32, i64) -> ptr, linkage(external);
3193func @use(ptr), linkage(external);
3194func @touch(ptr), linkage(external);
3195
3196func @g(), linkage(external) {
3197block0:
3198    %0 = alloca, size 64, align 16
3199    jump block1
3200block1:
3201    %1 = iconst.i32 88
3202    %2 = iconst.i64 64
3203    %3 = call @memset(%0, %1, %2) : (ptr, i32, i64) -> ptr
3204    %4 = global_addr @.Lstr.0
3205    %5 = call @strcpy(%0, %4) : (ptr, ptr) -> ptr
3206    TOUCH
3207    jump block2
3208block2:
3209    %6 = global_addr @.Lstr.1
3210    %7 = call @strcat(%0, %6) : (ptr, ptr) -> ptr
3211    %8 = global_addr @.Lstr.2
3212    %9 = call @strcat(%7, %8) : (ptr, ptr) -> ptr
3213    call @use(%9) : (ptr)
3214    return
3215}
3216"#;
3217        let out = folded(&text.replace("TOUCH", ""));
3218        assert!(!out.contains("call @strcat("), "{out}");
3219        assert!(out.contains("iconst.i64 11"), "the first goes where the terminator was, {out}");
3220        assert!(out.contains("iconst.i64 16"), "the second after the first, {out}");
3221        assert!(out.contains("call @use(%0)"), "{out}");
3222
3223        let out = folded(&text.replace("TOUCH", "call @touch(%0) : (ptr)"));
3224        assert_eq!(out.matches("call @strcat(").count(), 2, "{out}");
3225    }
3226
3227    /// `strncpy` of up to a string and its terminator is a copy of the count, of nothing is its
3228    /// destination, and of more than that is left alone because the rest is padded with zeros.
3229    #[test]
3230    fn strncpy_that_pads_nothing_is_a_copy() {
3231        let out = folded(
3232            r#"
3233global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3234
3235func @strncpy(ptr, ptr, i64) -> ptr, linkage(external);
3236func @use(ptr, ptr, ptr, ptr), linkage(external);
3237
3238func @g(ptr), linkage(external) {
3239block0(%0: ptr):
3240    %1 = global_addr @.Lstr.0
3241    %2 = iconst.i64 4
3242    %3 = call @strncpy(%0, %1, %2) : (ptr, ptr, i64) -> ptr
3243    %4 = iconst.i64 12
3244    %5 = call @strncpy(%0, %1, %4) : (ptr, ptr, i64) -> ptr
3245    %6 = iconst.i64 0
3246    %7 = call @strncpy(%0, %1, %6) : (ptr, ptr, i64) -> ptr
3247    %8 = iconst.i64 13
3248    %9 = call @strncpy(%0, %1, %8) : (ptr, ptr, i64) -> ptr
3249    call @use(%3, %5, %7, %9) : (ptr, ptr, ptr, ptr)
3250    return
3251}
3252"#,
3253        );
3254        assert_eq!(out.matches("call @memcpy(").count(), 2, "{out}");
3255        assert_eq!(out.matches("call @strncpy(").count(), 1, "thirteen pads a byte, {out}");
3256    }
3257
3258    /// `strcpy` of a pointer that may be any of several strings of one length is a copy of that
3259    /// many bytes and a terminator.
3260    #[test]
3261    fn strcpy_of_a_choice_of_one_length_is_a_copy() {
3262        let out = folded(
3263            r#"
3264global @.Lstr.0 : bytes 4 = { bytes "abc\00" }, align 1, linkage(internal), constant
3265global @.Lstr.1 : bytes 4 = { bytes "xyz\00" }, align 1, linkage(internal), constant
3266
3267func @strcpy(ptr, ptr) -> ptr, linkage(external);
3268func @use(ptr), linkage(external);
3269
3270func @g(ptr, i1), linkage(external) {
3271block0(%0: ptr, %1: i1):
3272    %2 = global_addr @.Lstr.0
3273    %3 = global_addr @.Lstr.1
3274    br_if %1, block1(%2), block1(%3)
3275block1(%4: ptr):
3276    %5 = call @strcpy(%0, %4) : (ptr, ptr) -> ptr
3277    call @use(%5) : (ptr)
3278    return
3279}
3280"#,
3281        );
3282        assert!(out.contains("call @memcpy("), "{out}");
3283        assert!(out.contains("iconst.i64 4"), "{out}");
3284    }
3285
3286    /// `strlen` of a local array the block has just written a string into is that string's length,
3287    /// from the front or from part way along, and a call in between leaves it a call.
3288    #[test]
3289    fn strlen_of_what_stores_just_wrote_is_its_length() {
3290        let text = r#"
3291func @strlen(ptr) -> i64, linkage(external);
3292func @use(i64, i64), linkage(external);
3293func @touch(), linkage(external);
3294
3295func @g(), linkage(external) {
3296block0:
3297    %0 = alloca, size 8, align 1
3298    %1 = alloca, size 8, align 1
3299    %2 = iconst.i8 110
3300    store %2 -> %0, align 1
3301    %3 = iconst.i64 1
3302    %4 = ptr_add %0, %3
3303    %5 = iconst.i8 116
3304    store %5 -> %4, align 1
3305    %6 = iconst.i64 2
3306    %7 = ptr_add %0, %6
3307    %8 = iconst.i8 0
3308    store %8 -> %7, align 1
3309    store %8 -> %1, align 1
3310    CALL
3311    %9 = call @strlen(%0) : (ptr) -> i64
3312    %10 = call @strlen(%4) : (ptr) -> i64
3313    call @use(%9, %10) : (i64, i64)
3314    return
3315}
3316"#;
3317        let out = folded(&text.replace("CALL", ""));
3318        assert!(!out.contains("call @strlen("), "{out}");
3319        assert!(out.contains("iconst.i64 2"), "{out}");
3320
3321        let out = folded(&text.replace("CALL", "call @touch() : ()"));
3322        assert_eq!(out.matches("call @strlen(").count(), 2, "{out}");
3323    }
3324
3325    /// `memcmp` of two constant objects is the sign of the first byte that differs, of a count of
3326    /// nothing is zero, and of a local array reads what was copied into it in front of the call,
3327    /// past an earlier comparison, which is `builtins/memcmp.c`.
3328    #[test]
3329    fn memcmp_of_bytes_known_in_front_of_it_is_their_order() {
3330        let text = r#"
3331global @.Lstr.0 : bytes 5 = { bytes "abcd\00" }, align 1, linkage(internal), constant
3332global @.Lstr.1 : bytes 5 = { bytes "efgh\00" }, align 1, linkage(internal), constant
3333global @.Lstr.2 : bytes 5 = { bytes "3141\00" }, align 1, linkage(internal), constant
3334
3335func @memcmp(ptr, ptr, i64) -> i32, linkage(external);
3336func @strcpy(ptr, ptr) -> ptr, linkage(external);
3337func @use(i32, i32, i32, i32, i32), linkage(external);
3338func @touch(ptr), linkage(external);
3339
3340func @g(ptr), linkage(external) {
3341block0(%0: ptr):
3342    %1 = global_addr @.Lstr.0
3343    %2 = global_addr @.Lstr.1
3344    %3 = iconst.i64 4
3345    %4 = call @memcmp(%1, %2, %3) : (ptr, ptr, i64) -> i32
3346    %5 = iconst.i64 0
3347    %6 = call @memcmp(%0, %2, %5) : (ptr, ptr, i64) -> i32
3348    %7 = alloca, size 8, align 1
3349    %8 = global_addr @.Lstr.2
3350    %9 = call @strcpy(%7, %8) : (ptr, ptr) -> ptr
3351    %10 = iconst.i64 2
3352    %11 = ptr_add %7, %10
3353    %12 = iconst.i64 1
3354    %13 = call @memcmp(%7, %11, %12) : (ptr, ptr, i64) -> i32
3355    TOUCH
3356    %14 = call @memcmp(%11, %7, %12) : (ptr, ptr, i64) -> i32
3357    %15 = call @memcmp(%0, %1, %3) : (ptr, ptr, i64) -> i32
3358    call @use(%4, %6, %13, %14, %15) : (i32, i32, i32, i32, i32)
3359    return
3360}
3361"#;
3362        let out = folded(&text.replace("TOUCH", ""));
3363        assert_eq!(out.matches("call @memcmp(").count(), 1, "the unknown one stays, {out}");
3364        assert!(out.contains("iconst.i32 -1"), "{out}");
3365        assert!(out.contains("iconst.i32 1"), "{out}");
3366
3367        let out = folded(&text.replace("TOUCH", "call @touch(%7) : (ptr)"));
3368        assert_eq!(out.matches("call @memcmp(").count(), 2, "{out}");
3369    }
3370
3371    /// The walk back from a comparison goes past an `if (...) abort ();` in front of it, since the
3372    /// arm that calls `abort` is not one control came from, and stops at a join of two arms that
3373    /// both come back.
3374    #[test]
3375    fn an_arm_that_calls_abort_is_not_a_way_in() {
3376        let text = r#"
3377global @.Lstr.0 : bytes 5 = { bytes "3141\00" }, align 1, linkage(internal), constant
3378
3379func @memcmp(ptr, ptr, i64) -> i32, linkage(external);
3380func @strcpy(ptr, ptr) -> ptr, linkage(external);
3381func @abort(), linkage(external);
3382func @other(), linkage(external);
3383func @use(i32), linkage(external);
3384
3385func @g(i1), linkage(external) {
3386block0(%0: i1):
3387    %1 = alloca, size 8, align 1
3388    %2 = global_addr @.Lstr.0
3389    %3 = call @strcpy(%1, %2) : (ptr, ptr) -> ptr
3390    br_if %0, block1, block2
3391block1:
3392    call @STOP() : ()
3393    jump block2
3394block2:
3395    %4 = iconst.i64 2
3396    %5 = ptr_add %1, %4
3397    %6 = iconst.i64 1
3398    %7 = call @memcmp(%1, %5, %6) : (ptr, ptr, i64) -> i32
3399    call @use(%7) : (i32)
3400    return
3401}
3402"#;
3403        let out = folded(&text.replace("STOP", "abort"));
3404        assert!(!out.contains("call @memcmp("), "{out}");
3405
3406        let out = folded(&text.replace("STOP", "other"));
3407        assert!(out.contains("call @memcmp("), "{out}");
3408    }
3409
3410    /// `strlen` of a pointer that may be either of two strings is their length where they share
3411    /// one, and stays a call where they do not.
3412    #[test]
3413    fn strlen_of_a_choice_between_literals_of_one_length_is_that_length() {
3414        let text = r#"
3415global @.Lstr.0 : bytes 4 = { bytes "abc\00" }, align 1, linkage(internal), constant
3416global @.Lstr.1 : bytes 4 = { bytes "xyz\00" }, align 1, linkage(internal), constant
3417global @.Lstr.2 : bytes 3 = { bytes "ab\00" }, align 1, linkage(internal), constant
3418
3419func @strlen(ptr) -> i64, linkage(external);
3420func @use(i64), linkage(external);
3421
3422func @g(i1), linkage(external) {
3423block0(%0: i1):
3424    %1 = global_addr @.LEFT
3425    %2 = global_addr @.Lstr.1
3426    br_if %0, block1(%1), block1(%2)
3427block1(%3: ptr):
3428    %4 = call @strlen(%3) : (ptr) -> i64
3429    call @use(%4) : (i64)
3430    return
3431}
3432"#;
3433        let same = folded(&text.replace(".LEFT", ".Lstr.0"));
3434        assert!(!same.contains("call @strlen("), "{same}");
3435        assert!(same.contains("iconst.i64 3"), "{same}");
3436
3437        let differing = folded(&text.replace(".LEFT", ".Lstr.2"));
3438        assert!(differing.contains("call @strlen("), "{differing}");
3439    }
3440
3441    /// `strlen` at a place inside a held string the program worked out is the length less the step,
3442    /// where the step cannot go past the terminator. A mask of seven fits inside eleven and a mask
3443    /// of fifteen does not, so only the first call goes.
3444    #[test]
3445    fn strlen_at_a_bounded_step_into_a_held_string_is_the_rest() {
3446        let out = folded(
3447            r#"
3448global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3449
3450func @strlen(ptr) -> i64, linkage(external);
3451func @use(i64, i64), linkage(external);
3452
3453func @g(i32), linkage(external) {
3454block0(%0: i32):
3455    %1 = global_addr @.Lstr.0
3456    %2 = iconst.i32 7
3457    %3 = and %0, %2
3458    %4 = sext.i64 %3
3459    %5 = ptr_add %1, %4
3460    %6 = call @strlen(%5) : (ptr) -> i64
3461    %7 = iconst.i32 15
3462    %8 = and %0, %7
3463    %9 = sext.i64 %8
3464    %10 = ptr_add %1, %9
3465    %11 = call @strlen(%10) : (ptr) -> i64
3466    call @use(%6, %11) : (i64, i64)
3467    return
3468}
3469"#,
3470        );
3471        assert_eq!(out.matches("call @strlen(").count(), 1, "{out}");
3472        assert!(out.contains("sub %6, %4"), "eleven less the step, {out}");
3473        assert!(out.contains("iconst.i64 11"), "{out}");
3474    }
3475
3476    /// `strnlen` stops at its count, and the count is what the answer is where nothing terminated
3477    /// the string inside it.
3478    #[test]
3479    fn strnlen_answers_the_count_where_the_string_runs_past_it() {
3480        let out = folded(
3481            r#"
3482global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3483
3484func @strnlen(ptr, i64) -> i64, linkage(external);
3485func @use(i64, i64), linkage(external);
3486
3487func @g(), linkage(external) {
3488block0:
3489    %0 = global_addr @.Lstr.0
3490    %1 = iconst.i64 3
3491    %2 = call @strnlen(%0, %1) : (ptr, i64) -> i64
3492    %3 = iconst.i64 40
3493    %4 = call @strnlen(%0, %3) : (ptr, i64) -> i64
3494    call @use(%2, %4) : (i64, i64)
3495    return
3496}
3497"#,
3498        );
3499        assert!(!out.contains("call @strnlen("), "{out}");
3500        assert!(out.contains("iconst.i64 3"), "the count came first, {out}");
3501        assert!(out.contains("iconst.i64 11"), "the terminator came first, {out}");
3502    }
3503
3504    /// A string the module holds is read no further than its terminator, so a count nothing is
3505    /// known about makes the answer the smaller of the two, and a count written as a negative number
3506    /// is a very large one.
3507    #[test]
3508    fn strnlen_of_a_string_this_module_holds_takes_any_count() {
3509        let out = folded(
3510            r#"
3511global @.Lstr.0 : bytes 4 = { bytes "123\00" }, align 1, linkage(internal), constant
3512global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3513
3514func @strnlen(ptr, i64) -> i64, linkage(external);
3515func @use(i64, i64, i64), linkage(external);
3516
3517func @g(i64), linkage(external) {
3518block0(%0: i64):
3519    %1 = global_addr @.Lstr.0
3520    %2 = call @strnlen(%1, %0) : (ptr, i64) -> i64
3521    %3 = iconst.i32 -2
3522    %4 = sext.i64 %3
3523    %5 = call @strnlen(%1, %4) : (ptr, i64) -> i64
3524    %6 = global_addr @.Lstr.1
3525    %7 = call @strnlen(%6, %0) : (ptr, i64) -> i64
3526    call @use(%2, %5, %7) : (i64, i64, i64)
3527    return
3528}
3529"#,
3530        );
3531        assert!(!out.contains("call @strnlen("), "{out}");
3532        assert!(out.contains("icmp ult %0"), "the count against the length, {out}");
3533        assert!(out.contains("select"), "and the smaller of the two, {out}");
3534        assert!(out.contains("iconst.i64 3"), "a negative count is past the terminator, {out}");
3535        assert!(out.contains("iconst.i64 0"), "an empty string is nothing to count, {out}");
3536    }
3537
3538    /// A count the source wrote as an `int` reaches the call widened, and the constant is under the
3539    /// widening rather than in the argument.
3540    #[test]
3541    fn a_count_that_was_widened_on_the_way_in_is_still_a_count() {
3542        let out = folded(
3543            r#"
3544global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3545
3546func @strnlen(ptr, i64) -> i64, linkage(external);
3547
3548func @g() -> i64, linkage(external) {
3549block0:
3550    %0 = global_addr @.Lstr.0
3551    %1 = iconst.i32 4
3552    %2 = sext.i64 %1
3553    %3 = call @strnlen(%0, %2) : (ptr, i64) -> i64
3554    return %3
3555}
3556"#,
3557        );
3558        assert!(!out.contains("call @strnlen("), "{out}");
3559        assert!(out.contains("iconst.i64 4"), "{out}");
3560    }
3561
3562    /// `memchr` reads a count rather than a string, so it finds a byte past the terminator, and it
3563    /// answers nowhere where the byte is outside the count.
3564    #[test]
3565    fn memchr_searches_the_object_rather_than_the_string_in_it() {
3566        let out = folded(
3567            r#"
3568global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3569
3570func @memchr(ptr, i32, i64) -> ptr, linkage(external);
3571func @use(ptr, ptr), linkage(external);
3572
3573func @g(), linkage(external) {
3574block0:
3575    %0 = global_addr @.Lstr.0
3576    %1 = iconst.i32 0
3577    %2 = iconst.i64 12
3578    %3 = call @memchr(%0, %1, %2) : (ptr, i32, i64) -> ptr
3579    %4 = iconst.i32 100
3580    %5 = iconst.i64 10
3581    %6 = call @memchr(%0, %4, %5) : (ptr, i32, i64) -> ptr
3582    call @use(%3, %6) : (ptr, ptr)
3583    return
3584}
3585"#,
3586        );
3587        assert!(!out.contains("call @memchr("), "{out}");
3588        assert!(out.contains("iconst.i64 11"), "the terminator is inside the count, {out}");
3589        assert!(out.contains("inttoptr.ptr "), "the d is one byte past the count, {out}");
3590    }
3591
3592    /// A count the object does not have that many bytes for is a read the compiler cannot see the
3593    /// end of, so the call stays.
3594    #[test]
3595    fn a_memchr_that_runs_off_the_object_is_left_alone() {
3596        let out = folded(
3597            r#"
3598global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3599
3600func @memchr(ptr, i32, i64) -> ptr, linkage(external);
3601
3602func @g() -> ptr, linkage(external) {
3603block0:
3604    %0 = global_addr @.Lstr.0
3605    %1 = iconst.i32 122
3606    %2 = iconst.i64 13
3607    %3 = call @memchr(%0, %1, %2) : (ptr, i32, i64) -> ptr
3608    return %3
3609}
3610"#,
3611        );
3612        assert!(out.contains("call @memchr("), "{out}");
3613    }
3614
3615    /// `strchr` finds the first, `strrchr` the last, and a search for the terminator finds it at the
3616    /// end rather than not at all.
3617    #[test]
3618    fn the_two_character_searches_answer_from_either_end() {
3619        let out = folded(
3620            r#"
3621global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3622
3623func @strchr(ptr, i32) -> ptr, linkage(external);
3624func @strrchr(ptr, i32) -> ptr, linkage(external);
3625func @use(ptr, ptr, ptr, ptr), linkage(external);
3626
3627func @g(), linkage(external) {
3628block0:
3629    %0 = global_addr @.Lstr.0
3630    %1 = iconst.i32 111
3631    %2 = call @strchr(%0, %1) : (ptr, i32) -> ptr
3632    %3 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
3633    %4 = iconst.i32 0
3634    %5 = call @strchr(%0, %4) : (ptr, i32) -> ptr
3635    %6 = iconst.i32 122
3636    %7 = call @strchr(%0, %6) : (ptr, i32) -> ptr
3637    call @use(%2, %3, %5, %7) : (ptr, ptr, ptr, ptr)
3638    return
3639}
3640"#,
3641        );
3642        assert!(!out.contains("call @strchr("), "{out}");
3643        assert!(!out.contains("call @strrchr("), "{out}");
3644        assert!(out.contains("iconst.i64 4"), "the first o, {out}");
3645        assert!(out.contains("iconst.i64 7"), "the last o, {out}");
3646        assert!(out.contains("iconst.i64 11"), "the terminator, {out}");
3647        assert!(out.contains("inttoptr.ptr "), "there is no z in it, {out}");
3648    }
3649
3650    /// The two comparisons answer one of minus one, zero and one, which is the sign the standard
3651    /// promises and nothing more.
3652    #[test]
3653    fn the_two_comparisons_answer_a_sign() {
3654        let out = folded(
3655            r#"
3656global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3657global @.Lstr.1 : bytes 6 = { bytes "hello\00" }, align 1, linkage(internal), constant
3658
3659func @strcmp(ptr, ptr) -> i32, linkage(external);
3660func @strncmp(ptr, ptr, i64) -> i32, linkage(external);
3661func @use(i32, i32, i32), linkage(external);
3662
3663func @g(), linkage(external) {
3664block0:
3665    %0 = global_addr @.Lstr.0
3666    %1 = global_addr @.Lstr.1
3667    %2 = call @strcmp(%0, %1) : (ptr, ptr) -> i32
3668    %3 = call @strcmp(%1, %0) : (ptr, ptr) -> i32
3669    %4 = iconst.i64 5
3670    %5 = call @strncmp(%0, %1, %4) : (ptr, ptr, i64) -> i32
3671    call @use(%2, %3, %5) : (i32, i32, i32)
3672    return
3673}
3674"#,
3675        );
3676        assert!(!out.contains("call @strcmp("), "{out}");
3677        assert!(!out.contains("call @strncmp("), "{out}");
3678        assert!(out.contains("iconst.i32 1"), "the longer one is the greater, {out}");
3679        assert!(out.contains("iconst.i32 -1"), "and the other way round, {out}");
3680        assert!(out.contains("iconst.i32 0"), "five bytes of each are the same, {out}");
3681    }
3682
3683    /// A comparison against the empty string reads the first byte of the other one, whichever side
3684    /// the empty string was written on.
3685    #[test]
3686    fn a_comparison_against_the_empty_string_is_a_read_of_one_byte() {
3687        let out = folded(
3688            r#"
3689global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3690
3691func @strcmp(ptr, ptr) -> i32, linkage(external);
3692func @use(i32, i32), linkage(external);
3693
3694func @g(ptr), linkage(external) {
3695block0(%0: ptr):
3696    %1 = global_addr @.Lstr.0
3697    %2 = call @strcmp(%0, %1) : (ptr, ptr) -> i32
3698    %3 = call @strcmp(%1, %0) : (ptr, ptr) -> i32
3699    call @use(%2, %3) : (i32, i32)
3700    return
3701}
3702"#,
3703        );
3704        assert!(!out.contains("call @strcmp("), "{out}");
3705        assert_eq!(out.matches("load.i8 %0").count(), 2, "one read for each call, {out}");
3706        assert_eq!(out.matches("zext").count(), 2, "read as an unsigned char, {out}");
3707        assert_eq!(out.matches("sub").count(), 2, "and the difference each way round, {out}");
3708    }
3709
3710    /// A comparison told to read no bytes reads neither string, and one told to read a single byte
3711    /// against a string this module holds is the difference between two bytes.
3712    #[test]
3713    fn a_short_count_settles_a_comparison_without_the_other_string() {
3714        let out = folded(
3715            r#"
3716global @.Lstr.0 : bytes 4 = { bytes "ozz\00" }, align 1, linkage(internal), constant
3717
3718func @strncmp(ptr, ptr, i64) -> i32, linkage(external);
3719func @use(i32, i32), linkage(external);
3720
3721func @g(ptr, ptr), linkage(external) {
3722block0(%0: ptr, %1: ptr):
3723    %2 = global_addr @.Lstr.0
3724    %3 = iconst.i32 0
3725    %4 = sext.i64 %3
3726    %5 = call @strncmp(%0, %1, %4) : (ptr, ptr, i64) -> i32
3727    %6 = iconst.i32 1
3728    %7 = sext.i64 %6
3729    %8 = call @strncmp(%2, %0, %7) : (ptr, ptr, i64) -> i32
3730    call @use(%5, %8) : (i32, i32)
3731    return
3732}
3733"#,
3734        );
3735        assert!(!out.contains("call @strncmp("), "{out}");
3736        assert!(out.contains("iconst.i32 0"), "no bytes to read is no difference, {out}");
3737        assert!(out.contains("load.i8 %0"), "one byte of the other string, {out}");
3738        assert!(out.contains("iconst.i32 111"), "against the first byte of this one, {out}");
3739    }
3740
3741    /// An index and a count the source worked out from constants are still the arithmetic when this
3742    /// pass looks, because nothing has folded them yet, and they are read as the answer they have.
3743    #[test]
3744    fn an_index_and_a_count_worked_out_from_constants_are_constants() {
3745        let out = folded(
3746            r#"
3747global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3748
3749func @strncmp(ptr, ptr, i64) -> i32, linkage(external);
3750func @use(i32), linkage(external);
3751
3752func @g(), linkage(external) {
3753block0:
3754    %0 = global_addr @.Lstr.0
3755    %1 = iconst.i64 1
3756    %2 = ptr_add %0, %1
3757    %3 = iconst.i32 1
3758    %4 = iconst.i32 3
3759    %5 = and %3, %4
3760    %6 = sext.i64 %5
3761    %7 = ptr_add %0, %6
3762    %8 = iconst.i32 2
3763    %9 = add.nsw %8, %3
3764    %10 = sext.i64 %9
3765    %11 = call @strncmp(%2, %7, %10) : (ptr, ptr, i64) -> i32
3766    call @use(%11) : (i32)
3767    return
3768}
3769"#,
3770        );
3771        assert!(!out.contains("call @strncmp("), "{out}");
3772    }
3773
3774    /// A comparison declared to answer something no wider than the byte it would read is left
3775    /// alone, because there is no room in it for the answer.
3776    #[test]
3777    fn a_comparison_with_no_room_for_a_byte_is_left_alone() {
3778        let out = folded(
3779            r#"
3780global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3781
3782func @strcmp(ptr, ptr) -> i8, linkage(external);
3783func @use(i8), linkage(external);
3784
3785func @g(ptr), linkage(external) {
3786block0(%0: ptr):
3787    %1 = global_addr @.Lstr.0
3788    %2 = call @strcmp(%0, %1) : (ptr, ptr) -> i8
3789    call @use(%2) : (i8)
3790    return
3791}
3792"#,
3793        );
3794        assert!(out.contains("call @strcmp("), "{out}");
3795    }
3796
3797    /// The two spans walk the same string with the test turned round.
3798    #[test]
3799    fn the_two_spans_are_one_walk_each_way() {
3800        let out = folded(
3801            r#"
3802global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3803global @.Lstr.1 : bytes 4 = { bytes "hel\00" }, align 1, linkage(internal), constant
3804global @.Lstr.2 : bytes 3 = { bytes "wz\00" }, align 1, linkage(internal), constant
3805
3806func @strspn(ptr, ptr) -> i64, linkage(external);
3807func @strcspn(ptr, ptr) -> i64, linkage(external);
3808func @use(i64, i64), linkage(external);
3809
3810func @g(), linkage(external) {
3811block0:
3812    %0 = global_addr @.Lstr.0
3813    %1 = global_addr @.Lstr.1
3814    %2 = call @strspn(%0, %1) : (ptr, ptr) -> i64
3815    %3 = global_addr @.Lstr.2
3816    %4 = call @strcspn(%0, %3) : (ptr, ptr) -> i64
3817    call @use(%2, %4) : (i64, i64)
3818    return
3819}
3820"#,
3821        );
3822        assert!(!out.contains("call @strspn("), "{out}");
3823        assert!(!out.contains("call @strcspn("), "{out}");
3824        assert!(out.contains("iconst.i64 4"), "hello stops at the o, {out}");
3825        assert!(out.contains("iconst.i64 6"), "the w is six bytes along, {out}");
3826    }
3827
3828    /// Nothing is inside an empty set, so `strspn(s, "")` is zero and `strcspn(s, "")` is the length
3829    /// of `s`, whatever `s` is.
3830    #[test]
3831    fn an_empty_set_is_a_span_of_nothing_or_of_all_of_it() {
3832        let out = folded(
3833            r#"
3834global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3835
3836func @strspn(ptr, ptr) -> i64, linkage(external);
3837func @strcspn(ptr, ptr) -> i64, linkage(external);
3838func @strlen(ptr) -> i64, linkage(external);
3839func @use(i64, i64), linkage(external);
3840
3841func @g(ptr), linkage(external) {
3842block0(%0: ptr):
3843    %1 = global_addr @.Lstr.0
3844    %2 = call @strspn(%0, %1) : (ptr, ptr) -> i64
3845    %3 = call @strcspn(%0, %1) : (ptr, ptr) -> i64
3846    call @use(%2, %3) : (i64, i64)
3847    return
3848}
3849"#,
3850        );
3851        assert!(!out.contains("call @strspn("), "{out}");
3852        assert!(!out.contains("call @strcspn("), "{out}");
3853        assert!(out.contains("call @strlen(%0)"), "{out}");
3854        assert!(out.contains("iconst.i64 0"), "{out}");
3855    }
3856
3857    /// A `strcspn` answering a width `strlen` does not answer is a fold that would leave a reader
3858    /// holding a number of the wrong size, so it does not happen.
3859    #[test]
3860    fn a_strcspn_of_another_width_than_strlen_is_left_alone() {
3861        let out = folded(
3862            r#"
3863global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3864
3865func @strcspn(ptr, ptr) -> i32, linkage(external);
3866func @strlen(ptr) -> i64, linkage(external);
3867
3868func @g(ptr) -> i32, linkage(external) {
3869block0(%0: ptr):
3870    %1 = global_addr @.Lstr.0
3871    %2 = call @strcspn(%0, %1) : (ptr, ptr) -> i32
3872    return %2
3873}
3874"#,
3875        );
3876        assert!(out.contains("call @strcspn("), "{out}");
3877    }
3878
3879    /// `strpbrk` of a set of one character is a search for that character, and of an empty set is
3880    /// nowhere at all.
3881    #[test]
3882    fn strpbrk_of_a_short_set_is_a_search_or_an_answer() {
3883        let out = folded(
3884            r#"
3885global @.Lstr.0 : bytes 2 = { bytes "w\00" }, align 1, linkage(internal), constant
3886global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
3887
3888func @strpbrk(ptr, ptr) -> ptr, linkage(external);
3889func @strchr(ptr, i32) -> ptr, linkage(external);
3890func @use(ptr, ptr), linkage(external);
3891
3892func @g(ptr), linkage(external) {
3893block0(%0: ptr):
3894    %1 = global_addr @.Lstr.0
3895    %2 = call @strpbrk(%0, %1) : (ptr, ptr) -> ptr
3896    %3 = global_addr @.Lstr.1
3897    %4 = call @strpbrk(%0, %3) : (ptr, ptr) -> ptr
3898    call @use(%2, %4) : (ptr, ptr)
3899    return
3900}
3901"#,
3902        );
3903        assert!(!out.contains("call @strpbrk("), "{out}");
3904        assert!(out.contains("call @strchr(%0, "), "{out}");
3905        assert!(out.contains("iconst.i32 119"), "{out}");
3906        assert!(out.contains("inttoptr.ptr "), "the empty set is nowhere, {out}");
3907    }
3908
3909    /// Two strings this module holds answer `strpbrk` without either call.
3910    #[test]
3911    fn strpbrk_over_two_strings_this_module_holds_is_a_place() {
3912        let out = folded(
3913            r#"
3914global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3915global @.Lstr.1 : bytes 3 = { bytes "wz\00" }, align 1, linkage(internal), constant
3916global @.Lstr.2 : bytes 3 = { bytes "qz\00" }, align 1, linkage(internal), constant
3917
3918func @strpbrk(ptr, ptr) -> ptr, linkage(external);
3919func @use(ptr, ptr), linkage(external);
3920
3921func @g(), linkage(external) {
3922block0:
3923    %0 = global_addr @.Lstr.0
3924    %1 = global_addr @.Lstr.1
3925    %2 = call @strpbrk(%0, %1) : (ptr, ptr) -> ptr
3926    %3 = global_addr @.Lstr.2
3927    %4 = call @strpbrk(%0, %3) : (ptr, ptr) -> ptr
3928    call @use(%2, %4) : (ptr, ptr)
3929    return
3930}
3931"#,
3932        );
3933        assert!(!out.contains("call @strpbrk("), "{out}");
3934        assert!(out.contains("iconst.i64 6"), "the w is six bytes along, {out}");
3935        assert!(out.contains("inttoptr.ptr "), "there is neither a q nor a z in it, {out}");
3936    }
3937
3938    /// `index` and `rindex` are the same two searches under their older names.
3939    #[test]
3940    fn the_older_spellings_of_the_two_searches_are_folded_as_well() {
3941        let out = folded(
3942            r#"
3943global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
3944
3945func @index(ptr, i32) -> ptr, linkage(external);
3946func @rindex(ptr, i32) -> ptr, linkage(external);
3947func @use(ptr, ptr), linkage(external);
3948
3949func @g(), linkage(external) {
3950block0:
3951    %0 = global_addr @.Lstr.0
3952    %1 = iconst.i32 111
3953    %2 = call @index(%0, %1) : (ptr, i32) -> ptr
3954    %3 = call @rindex(%0, %1) : (ptr, i32) -> ptr
3955    call @use(%2, %3) : (ptr, ptr)
3956    return
3957}
3958"#,
3959        );
3960        assert!(!out.contains("call @index("), "{out}");
3961        assert!(!out.contains("call @rindex("), "{out}");
3962        assert!(out.contains("iconst.i64 4"), "the first o, {out}");
3963        assert!(out.contains("iconst.i64 7"), "the last o, {out}");
3964    }
3965
3966    /// A search from the right for the terminator is a search from the left for it, because a
3967    /// string has one terminator and both walks find that one.
3968    #[test]
3969    fn a_strrchr_of_the_terminator_is_a_strchr_of_it() {
3970        let out = folded(
3971            r#"
3972func @strrchr(ptr, i32) -> ptr, linkage(external);
3973
3974func @g(ptr) -> ptr, linkage(external) {
3975block0(%0: ptr):
3976    %1 = iconst.i32 0
3977    %2 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
3978    return %2
3979}
3980"#,
3981        );
3982        assert!(!out.contains("call @strrchr("), "{out}");
3983        assert!(out.contains("call @strchr(%0, "), "{out}");
3984    }
3985
3986    /// A search from the right for anything else needs the string, since where the last one is
3987    /// depends on what is in it.
3988    #[test]
3989    fn a_strrchr_of_another_character_needs_the_string() {
3990        let out = folded(
3991            r#"
3992func @strrchr(ptr, i32) -> ptr, linkage(external);
3993
3994func @g(ptr) -> ptr, linkage(external) {
3995block0(%0: ptr):
3996    %1 = iconst.i32 111
3997    %2 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
3998    return %2
3999}
4000"#,
4001        );
4002        assert!(out.contains("call @strrchr("), "{out}");
4003    }
4004
4005    /// A declaration of the wrong shape is a function of the program's own, whatever it is called.
4006    #[test]
4007    fn a_strlen_that_answers_nothing_is_not_the_one_the_library_has() {
4008        let out = folded(
4009            r#"
4010global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
4011
4012func @strlen(ptr), linkage(external);
4013
4014func @g(), linkage(external) {
4015block0:
4016    %0 = global_addr @.Lstr.0
4017    call @strlen(%0) : (ptr)
4018    return
4019}
4020"#,
4021        );
4022        assert!(out.contains("call @strlen("), "{out}");
4023    }
4024
4025    /// A checking copy whose count is known to fit is the plain copy, one that is not known to
4026    /// fit keeps its check, and `__mempcpy_chk` whose answer nothing reads keeps its check on the
4027    /// copy that has no answer to work out.
4028    #[test]
4029    fn a_checking_copy_that_fits_is_the_plain_copy() {
4030        let out = folded(
4031            r#"
4032func @__memcpy_chk(ptr, ptr, i64, i64) -> ptr, linkage(external);
4033func @__mempcpy_chk(ptr, ptr, i64, i64) -> ptr, linkage(external);
4034func @use(ptr, ptr), linkage(external);
4035
4036func @g(ptr, ptr, i64), linkage(external) {
4037block0(%0: ptr, %1: ptr, %2: i64):
4038    %3 = iconst.i64 4
4039    %4 = iconst.i64 32
4040    %5 = call @__memcpy_chk(%0, %1, %3, %4) : (ptr, ptr, i64, i64) -> ptr
4041    %6 = iconst.i64 40
4042    %7 = call @__memcpy_chk(%0, %1, %6, %4) : (ptr, ptr, i64, i64) -> ptr
4043    %8 = call @__mempcpy_chk(%0, %1, %2, %4) : (ptr, ptr, i64, i64) -> ptr
4044    call @use(%5, %7) : (ptr, ptr)
4045    return
4046}
4047"#,
4048        );
4049        assert!(out.contains("call @memcpy(%0, %1, %3)"), "four bytes fit in thirty two, {out}");
4050        assert!(out.contains("call @__memcpy_chk(%0, %1, %6, %4)"), "forty do not, {out}");
4051        assert!(out.contains("call @__memcpy_chk(%0, %1, %2, %4)"), "nothing read the end, {out}");
4052        assert!(!out.contains("call @__mempcpy_chk("), "{out}");
4053    }
4054
4055    /// `__stpcpy_chk` of a string that fits is `stpcpy`, which is a `memcpy` of the string and its
4056    /// terminator answering the end of the copy, and of a string nothing is known about it stays,
4057    /// or becomes `__strcpy_chk` where its answer is not read.
4058    #[test]
4059    fn a_checking_string_copy_goes_as_far_as_the_string_is_known() {
4060        let out = folded(
4061            r#"
4062global @.Lstr.0 : bytes 6 = { bytes "abcde\00" }, align 1, linkage(internal), constant
4063
4064func @__stpcpy_chk(ptr, ptr, i64) -> ptr, linkage(external);
4065func @use(ptr, ptr), linkage(external);
4066
4067func @g(ptr, ptr), linkage(external) {
4068block0(%0: ptr, %1: ptr):
4069    %2 = global_addr @.Lstr.0
4070    %3 = iconst.i64 32
4071    %4 = call @__stpcpy_chk(%0, %2, %3) : (ptr, ptr, i64) -> ptr
4072    %5 = call @__stpcpy_chk(%0, %1, %3) : (ptr, ptr, i64) -> ptr
4073    %6 = call @__stpcpy_chk(%0, %1, %3) : (ptr, ptr, i64) -> ptr
4074    call @use(%4, %5) : (ptr, ptr)
4075    return
4076}
4077"#,
4078        );
4079        assert!(out.contains("call @memcpy(%0, %2, "), "{out}");
4080        assert!(out.contains("iconst.i64 6"), "five bytes and a terminator, {out}");
4081        assert!(out.contains("ptr_add %0"), "the answer is the end of the copy, {out}");
4082        assert!(out.contains("call @__stpcpy_chk(%0, %1, %3)"), "{out}");
4083        assert!(out.contains("call @__strcpy_chk(%0, %1, %3)"), "{out}");
4084    }
4085
4086    /// A string known not to fit is still a count, so `__strcpy_chk` of it is `__memcpy_chk` of
4087    /// the string and its terminator, and the library checks a number.
4088    #[test]
4089    fn a_checking_string_copy_that_does_not_fit_is_a_checking_copy_of_a_count() {
4090        let out = folded(
4091            r#"
4092global @.Lstr.0 : bytes 6 = { bytes "abcde\00" }, align 1, linkage(internal), constant
4093
4094func @__strcpy_chk(ptr, ptr, i64) -> ptr, linkage(external);
4095func @use(ptr), linkage(external);
4096
4097func @g(ptr), linkage(external) {
4098block0(%0: ptr):
4099    %1 = global_addr @.Lstr.0
4100    %2 = iconst.i64 4
4101    %3 = call @__strcpy_chk(%0, %1, %2) : (ptr, ptr, i64) -> ptr
4102    call @use(%3) : (ptr)
4103    return
4104}
4105"#,
4106        );
4107        assert!(out.contains("call @__memcpy_chk(%0, %1, "), "{out}");
4108        assert!(out.contains("iconst.i64 6"), "{out}");
4109    }
4110
4111    /// Appending the empty string, or no bytes of any string, answers the destination, and
4112    /// `__strncat_chk` whose count is no limit on the string is `__strcat_chk`.
4113    #[test]
4114    fn a_checking_append_of_nothing_is_the_destination() {
4115        let out = folded(
4116            r#"
4117global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
4118global @.Lstr.1 : bytes 4 = { bytes "abc\00" }, align 1, linkage(internal), constant
4119
4120func @__strcat_chk(ptr, ptr, i64) -> ptr, linkage(external);
4121func @__strncat_chk(ptr, ptr, i64, i64) -> ptr, linkage(external);
4122func @use(ptr, ptr, ptr, ptr), linkage(external);
4123
4124func @g(ptr, ptr), linkage(external) {
4125block0(%0: ptr, %1: ptr):
4126    %2 = global_addr @.Lstr.0
4127    %3 = global_addr @.Lstr.1
4128    %4 = iconst.i64 32
4129    %5 = call @__strcat_chk(%0, %2, %4) : (ptr, ptr, i64) -> ptr
4130    %6 = iconst.i64 0
4131    %7 = call @__strncat_chk(%0, %1, %6, %4) : (ptr, ptr, i64, i64) -> ptr
4132    %8 = iconst.i64 5
4133    %9 = call @__strncat_chk(%0, %3, %8, %4) : (ptr, ptr, i64, i64) -> ptr
4134    %10 = iconst.i64 2
4135    %11 = call @__strncat_chk(%0, %3, %10, %4) : (ptr, ptr, i64, i64) -> ptr
4136    call @use(%5, %7, %9, %11) : (ptr, ptr, ptr, ptr)
4137    return
4138}
4139"#,
4140        );
4141        assert!(out.contains("call @use(%0, %0, "), "{out}");
4142        assert_eq!(
4143            out.matches("call @__strcat_chk(%0, ").count(),
4144            1,
4145            "five is no limit on three, {out}"
4146        );
4147        assert_eq!(out.matches("call @__strncat_chk(%0, ").count(), 1, "two is, {out}");
4148    }
4149
4150    /// `__sprintf_chk` of a format with nothing to convert that fits is `sprintf`, which is
4151    /// `strcpy` answering the length, which is `memcpy`. A format with a conversion in it writes a
4152    /// length nothing knows and keeps its check.
4153    #[test]
4154    fn a_checking_sprintf_of_a_known_string_is_a_copy() {
4155        let out = folded(
4156            r#"
4157global @.Lstr.0 : bytes 6 = { bytes "hello\00" }, align 1, linkage(internal), constant
4158global @.Lstr.1 : bytes 3 = { bytes "%d\00" }, align 1, linkage(internal), constant
4159
4160func @__sprintf_chk(ptr, i32, i64, ptr, ...) -> i32, linkage(external);
4161func @use(i32, i32), linkage(external);
4162
4163func @g(ptr, i32), linkage(external) {
4164block0(%0: ptr, %1: i32):
4165    %2 = global_addr @.Lstr.0
4166    %3 = global_addr @.Lstr.1
4167    %4 = iconst.i32 0
4168    %5 = iconst.i64 32
4169    %6 = call @__sprintf_chk(%0, %4, %5, %2) : (ptr, i32, i64, ptr, ...) -> i32
4170    %7 = call @__sprintf_chk(%0, %4, %5, %3, %1) : (ptr, i32, i64, ptr, ...) -> i32
4171    call @use(%6, %7) : (i32, i32)
4172    return
4173}
4174"#,
4175        );
4176        assert!(out.contains("call @memcpy(%0, %2, "), "{out}");
4177        assert!(out.contains("iconst.i32 5"), "the length is the answer, {out}");
4178        assert!(out.contains("call @__sprintf_chk(%0, %4, %5, %3, %1)"), "{out}");
4179    }
4180
4181    /// `__snprintf_chk` whose bound fits is `snprintf` with what it was passed beyond its format
4182    /// still passed, and a flag asking for more checking keeps the check on a format it would read.
4183    #[test]
4184    fn a_checking_snprintf_keeps_its_arguments_and_loses_its_check() {
4185        let out = folded(
4186            r#"
4187global @.Lstr.0 : bytes 3 = { bytes "%d\00" }, align 1, linkage(internal), constant
4188
4189func @__snprintf_chk(ptr, i64, i32, i64, ptr, ...) -> i32, linkage(external);
4190func @use(i32, i32), linkage(external);
4191
4192func @g(ptr, i32), linkage(external) {
4193block0(%0: ptr, %1: i32):
4194    %2 = global_addr @.Lstr.0
4195    %3 = iconst.i64 8
4196    %4 = iconst.i32 0
4197    %5 = iconst.i64 32
4198    %6 = call @__snprintf_chk(%0, %3, %4, %5, %2, %1) : (ptr, i64, i32, i64, ptr, ...) -> i32
4199    %7 = iconst.i32 1
4200    %8 = call @__snprintf_chk(%0, %3, %7, %5, %2, %1) : (ptr, i64, i32, i64, ptr, ...) -> i32
4201    call @use(%6, %8) : (i32, i32)
4202    return
4203}
4204"#,
4205        );
4206        assert!(
4207            out.contains("call @snprintf(%0, %3, %2, %1) : (ptr, i64, ptr, ...) -> i32"),
4208            "{out}"
4209        );
4210        assert!(out.contains("call @__snprintf_chk(%0, %3, %7, %5, %2, %1)"), "{out}");
4211    }
4212
4213    /// A program that declared the plain function as something else keeps its checking call.
4214    #[test]
4215    fn a_plain_function_of_another_shape_keeps_the_check() {
4216        let out = folded(
4217            r#"
4218func @__memcpy_chk(ptr, ptr, i64, i64) -> ptr, linkage(external);
4219func @memcpy(ptr, ptr, i32) -> ptr, linkage(external);
4220func @use(ptr), linkage(external);
4221
4222func @g(ptr, ptr), linkage(external) {
4223block0(%0: ptr, %1: ptr):
4224    %2 = iconst.i64 4
4225    %3 = iconst.i64 32
4226    %4 = call @__memcpy_chk(%0, %1, %2, %3) : (ptr, ptr, i64, i64) -> ptr
4227    call @use(%4) : (ptr)
4228    return
4229}
4230"#,
4231        );
4232        assert!(out.contains("call @__memcpy_chk("), "{out}");
4233    }
4234
4235    /// `mempcpy` is `memcpy` answering the end of the copy, and a `mempcpy` into the end of another
4236    /// one is the second of two copies whose destination is the first one's answer, which is a
4237    /// value the first fold took away and the second has to be told about.
4238    #[test]
4239    fn a_copy_into_the_end_of_a_copy_names_the_end_the_first_fold_wrote() {
4240        let out = folded(
4241            r#"
4242global @.Lstr.0 : bytes 8 = { bytes "abcdEFG\00" }, align 1, linkage(internal), constant
4243global @.Lstr.1 : bytes 4 = { bytes "efg\00" }, align 1, linkage(internal), constant
4244
4245func @mempcpy(ptr, ptr, i64) -> ptr, linkage(external);
4246func @use(ptr), linkage(external);
4247
4248func @g(ptr), linkage(external) {
4249block0(%0: ptr):
4250    %1 = global_addr @.Lstr.0
4251    %2 = global_addr @.Lstr.1
4252    %3 = iconst.i64 4
4253    %4 = call @mempcpy(%0, %1, %3) : (ptr, ptr, i64) -> ptr
4254    %5 = call @mempcpy(%4, %2, %3) : (ptr, ptr, i64) -> ptr
4255    call @use(%5) : (ptr)
4256    return
4257}
4258"#,
4259        );
4260        assert!(!out.contains("call @mempcpy("), "{out}");
4261        assert_eq!(out.matches("call @memcpy(").count(), 2, "{out}");
4262        assert_eq!(out.matches("ptr_add").count(), 2, "{out}");
4263    }
4264
4265    /// `strncat` whose count is no limit on a string whose length is known is `strcat`, and one
4266    /// whose count is short of it keeps the count.
4267    #[test]
4268    fn a_counted_append_of_all_of_a_known_string_is_the_uncounted_one() {
4269        let out = folded(
4270            r#"
4271global @.Lstr.0 : bytes 4 = { bytes "foo\00" }, align 1, linkage(internal), constant
4272
4273func @strncat(ptr, ptr, i64) -> ptr, linkage(external);
4274func @use(ptr, ptr), linkage(external);
4275
4276func @g(ptr), linkage(external) {
4277block0(%0: ptr):
4278    %1 = global_addr @.Lstr.0
4279    %2 = iconst.i64 3
4280    %3 = call @strncat(%0, %1, %2) : (ptr, ptr, i64) -> ptr
4281    %4 = iconst.i64 2
4282    %5 = call @strncat(%0, %1, %4) : (ptr, ptr, i64) -> ptr
4283    call @use(%3, %5) : (ptr, ptr)
4284    return
4285}
4286"#,
4287        );
4288        assert_eq!(out.matches("call @strcat(%0, %1)").count(), 1, "{out}");
4289        assert_eq!(out.matches("call @strncat(").count(), 1, "{out}");
4290    }
4291
4292    /// A count that is one of two numbers fits where the larger of the two does, which is
4293    /// `l1 ? sizeof (buf) : 4` in `builtins/pr23484-chk.c`.
4294    #[test]
4295    fn a_count_fits_where_the_largest_it_may_be_fits() {
4296        let text = r#"
4297func @__memcpy_chk(ptr, ptr, i64, i64) -> ptr, linkage(external);
4298func @use(ptr), linkage(external);
4299
4300func @g(ptr, ptr, i1), linkage(external) {
4301block0(%0: ptr, %1: ptr, %2: i1):
4302    %3 = iconst.i64 8
4303    %4 = iconst.i64 4
4304    br_if %2, block1(%3), block1(%4)
4305block1(%5: i64):
4306    %6 = iconst.i64 SIZE
4307    %7 = call @__memcpy_chk(%0, %1, %5, %6) : (ptr, ptr, i64, i64) -> ptr
4308    call @use(%7) : (ptr)
4309    return
4310}
4311"#;
4312        let fits = folded(&text.replace("SIZE", "8"));
4313        assert!(fits.contains("call @memcpy("), "{fits}");
4314        let short = folded(&text.replace("SIZE", "7"));
4315        assert!(short.contains("call @__memcpy_chk("), "{short}");
4316    }
4317
4318    /// `floor ((double) f)` is `floorf (f)` widened, whether the answer is kept as a `double` or
4319    /// taken back to a `float`, and `sin` of the same widened `float` stays a call to `sin`.
4320    #[test]
4321    fn rounding_a_widened_float_is_done_in_float() {
4322        let text = r#"
4323func @floor(f64) -> f64, linkage(external);
4324func @sin(f64) -> f64, linkage(external);
4325func @use(f64, f64, f64), linkage(external);
4326
4327func @g(f32, f64), linkage(external) {
4328block0(%0: f32, %1: f64):
4329    %2 = fpext.f64 %0
4330    %3 = call @floor(%2) : (f64) -> f64
4331    %4 = call @sin(%2) : (f64) -> f64
4332    %5 = call @floor(%1) : (f64) -> f64
4333    call @use(%3, %4, %5) : (f64, f64, f64)
4334    return
4335}
4336"#;
4337        let out = folded(text);
4338        assert!(out.contains("call @floorf(%0) : (f32) -> f32"), "{out}");
4339        assert_eq!(out.matches("call @floor(").count(), 1, "the double one stays, {out}");
4340        assert_eq!(out.matches("call @sin(").count(), 1, "{out}");
4341    }
4342}