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//! # What a call has to be
60//!
61//! For the printf family, its result has to be read by nothing. `printf` answers the number of
62//! characters written and `puts` answers a non-negative number that is not that count, so a program
63//! looking at the answer is a program this may not touch. The str and mem families are the other
64//! way round: the answer is the whole point of the call and the fold produces it, so a program
65//! reading it is the ordinary case.
66//!
67//! It has to give back one value of the kind its name says it does. A program that declared
68//! `strchr` as something returning two values, or a number, declared a function of its own and a
69//! pointer into a string literal is not what it answers.
70//!
71//! The name has to be the one the source spelled rather than the one the object file will carry.
72//! `extern char *strstr (const char *, const char *) __asm ("my_strstr");` is a declaration of
73//! `strstr`, and a compiler that reads the symbol alone sees a call to a function it knows nothing
74//! about. So the callee is looked up through [`rucc_ir::Func::spelled`], and a call this leaves
75//! behind is a call to whatever symbol the module says that name has, which is the rename again
76//! read from the other end.
77//!
78//! The name has to be one this module does not define. A translation unit holding the body of its
79//! own `fputs` means that body, which is the rule [`crate::heap`] applies to `malloc` and for the
80//! same reason.
81//!
82//! The function must not carry memory SSA yet, which where this runs it does not. Memory is
83//! threaded by [`crate::number`], that pass is in the function pipeline, and this runs before the
84//! pipeline starts. The check is here anyway, because a call with a memory operand rewritten into
85//! one without would be a use of a value nothing defines.
86//!
87//! # Where it runs, and why it is not a rule
88//!
89//! Section 20.2 asks for folds like these to be rules in the rewrite DSL with the callee's identity
90//! in the pattern, and most of them can be. These cannot. A rule rewrites one instruction into
91//! instructions, and two of the rewrites here need something no rule has: the name `puts` has to be
92//! interned before a call can name it, and `printf("hello world\n")` has to leave behind a string
93//! that is not in the module yet, because "hello world" with a terminator is not a suffix of
94//! "hello world\n" with one. So this is a module at a time transformation beside [`crate::ipcp`]
95//! and [`crate::ipasra`], which is where the interner and the module both are.
96//!
97//! `-O1` and above, which is one level below where those two run. gcc folds these at `-O1`, the
98//! torture programs are compiled at every level from `-O1` up, and the fold makes the program
99//! smaller as well as faster, so there is no level above `-O0` where declining it is right.
100//!
101//! Off under `-fno-builtin` and `-ffreestanding`, which is the flag pair section 20.1 describes,
102//! and off for one name at a time under `-fno-builtin-<name>`. A freestanding program left with a
103//! call to a `puts` it never wrote is a link failure, and that is the whole reason the flag exists.
104
105use std::collections::{HashMap, HashSet};
106
107use rucc_base::{Interner, Symbol};
108use rucc_ir::{
109    CallInfo, Datum, Def, Extra, Func, FuncId, Global, Imm, Inst, InstData, Linkage, Module,
110    Opcode, Pic, Signature, SymbolRef, Type, Value,
111};
112
113use crate::extents::vouched;
114use crate::{Cfg, Fuel, Stats, uses};
115
116/// What the pass is called in `-fopt-info` and `-fpass-fuel=`.
117pub const NAME: &str = "libcall";
118
119/// How many block parameters deep the walk that answers "what string is this" goes.
120///
121/// A conditional expression whose arms are two literals is one level, which is what
122/// `builtins/fputs.c` writes twice. Four is room for that nested three deep and is the bound that
123/// stops a walk which would otherwise go round a loop forever. The same number bounds the walk
124/// down a chain of `ptr_add`, where one level is one index written in the source.
125const DEPTH: u32 = 4;
126
127/// The names a fold may leave behind, sorted.
128const REPLACEMENTS: [&str; 7] = ["fputc", "fputs", "fwrite", "putchar", "puts", "strchr", "strlen"];
129
130/// The names a fold reads, sorted.
131const SOURCES: [&str; 19] = [
132    "fprintf",
133    "fprintf_unlocked",
134    "fputs",
135    "fputs_unlocked",
136    "index",
137    "memchr",
138    "printf",
139    "printf_unlocked",
140    "rindex",
141    "strchr",
142    "strcmp",
143    "strcspn",
144    "strlen",
145    "strncmp",
146    "strnlen",
147    "strpbrk",
148    "strrchr",
149    "strspn",
150    "strstr",
151];
152
153/// What the compiler worked out a call writes, which is what it is replaced by.
154#[derive(Debug, Clone, PartialEq, Eq)]
155enum Plan {
156    /// It writes nothing, so it goes and nothing takes its place.
157    Drop,
158    /// The answer is a place in an argument the call was given, or nowhere at all, and that answer
159    /// takes the place of the call's result.
160    Answer(Answer),
161    /// This call takes its place.
162    Swap {
163        /// The symbol the replacement names, which is what the module calls that function.
164        callee: Symbol,
165        /// What that function takes and returns.
166        signature: Signature,
167        /// What to pass it.
168        args: Vec<Argument>,
169    },
170}
171
172/// What a call that answers rather than writes was going to answer.
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174enum Answer {
175    /// That many bytes along from a value the call was handed.
176    Along(Value, u64),
177    /// Nowhere in it, which is a null pointer.
178    Nowhere,
179    /// That number, in whatever type the call was declared to give back.
180    ///
181    /// The type is read off the call rather than worked out from the name, because a program that
182    /// declared `strlen` as something returning an `int` gets an `int`, and a constant of the
183    /// width the call already had is the only one that can take its place.
184    Number(i128),
185}
186
187/// Which of the places a character appears in a string a search wants.
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189enum Side {
190    /// `strchr`.
191    First,
192    /// `strrchr`.
193    Last,
194}
195
196/// Which way round the test in a span is.
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198enum Set {
199    /// `strspn`, which walks while the character is one of the set.
200    Inside,
201    /// `strcspn`, which walks while it is not.
202    Outside,
203}
204
205/// One argument of a replacement call.
206#[derive(Debug, Clone, PartialEq, Eq)]
207enum Argument {
208    /// A value the call being replaced already had.
209    Have(Value),
210    /// An `int` constant, which in every case here is a character.
211    Char(u8),
212    /// A `size_t` constant, which in every case here is a count of bytes.
213    Count(u64),
214    /// The address of a read only object holding these bytes and a terminator.
215    Text(Vec<u8>),
216}
217
218/// What this module already says about each name a fold may leave behind.
219///
220/// The verifier holds that a call to a name the module declares carries that name's own signature,
221/// so a program that declared `fwrite` through `<stdio.h>` decides what a call to it looks like and
222/// a program that declared it as something else stops the fold. The alternative is a fold that
223/// produces IR the verifier refuses, which is a compiler that crashes on a program gcc compiles.
224struct Shapes {
225    /// The symbol a call to that name has to carry and the signature it has to have, and `None`
226    /// where no call may name it.
227    held: HashMap<&'static str, Option<(Symbol, Signature)>>,
228}
229
230impl Shapes {
231    /// Reads the module's answer for each of the names a fold may leave behind.
232    fn of(module: &Module, names: &mut Interner) -> Self {
233        let mut held: HashMap<&'static str, Option<(Symbol, Signature)>> = REPLACEMENTS
234            .iter()
235            .map(|&name| (name, Some((names.intern(name), canonical(module, name)))))
236            .collect();
237        for id in module.funcs() {
238            // The name the source gave it, so that a module which renamed `puts` is left with a
239            // call to the symbol it renamed it to rather than one to a `puts` it never declared.
240            let func = &module[id];
241            let spelled = func.spelled.unwrap_or(func.name);
242            let Some(slot) = held.get_mut(names.resolve(spelled)) else { continue };
243            let declared = func.signature();
244            let agrees = slot.as_ref().is_some_and(|(_, want)| {
245                !declared.variadic
246                    && declared.param_types().eq(want.param_types())
247                    && declared.return_types().eq(want.return_types())
248            });
249            *slot = agrees.then(|| (func.name, declared.clone()));
250        }
251        // A variable or a second name for something else is not a function to call, whatever it is
252        // spelled.
253        for id in module.globals() {
254            if let Some(slot) = held.get_mut(names.resolve(module[id].name)) {
255                *slot = None;
256            }
257        }
258        for id in module.aliases() {
259            if let Some(slot) = held.get_mut(names.resolve(module[id].name)) {
260                *slot = None;
261            }
262        }
263        Self { held }
264    }
265
266    /// What a call to that name carries, or `None` where this module does not allow one.
267    fn get(&self, name: &'static str) -> Option<(Symbol, Signature)> {
268        self.held.get(name)?.clone()
269    }
270}
271
272/// The signature a call to that name carries where nothing in the module declared it.
273///
274/// What the frontend already writes, which is how `__builtin_putchar` reaches `putchar` in a
275/// program that never named it.
276fn canonical(module: &Module, name: &str) -> Signature {
277    let int = int();
278    let size = size(module);
279    match name {
280        "puts" => Signature::new().with_params(&[Type::PTR]).with_returns(&[int]),
281        "putchar" => Signature::new().with_params(&[int]).with_returns(&[int]),
282        "fputc" => Signature::new().with_params(&[int, Type::PTR]).with_returns(&[int]),
283        "fputs" => Signature::new().with_params(&[Type::PTR, Type::PTR]).with_returns(&[int]),
284        "strchr" => Signature::new().with_params(&[Type::PTR, int]).with_returns(&[Type::PTR]),
285        "strlen" => Signature::new().with_params(&[Type::PTR]).with_returns(&[size]),
286        // `fwrite`, the one that is told how many bytes to write rather than going looking for a
287        // terminator, and the only one of the five whose types are the target's rather than fixed.
288        _ => {
289            Signature::new().with_params(&[Type::PTR, size, size, Type::PTR]).with_returns(&[size])
290        }
291    }
292}
293
294/// The type an `int` is in the IR.
295///
296/// Thirty two bits on every target this compiler has a back end for, which is why it is written
297/// here rather than asked of the layout. The day one of them says otherwise, a fold under this rule
298/// would hand `putchar` the wrong width, and this is the one place that would have to change.
299const fn int() -> Type {
300    Type::int(32)
301}
302
303/// The type a `size_t` is on the target this module is for.
304fn size(module: &Module) -> Type {
305    Type::int(module.datalayout.pointer_bits)
306}
307
308/// Folds every call in the module whose output the compiler can work out.
309///
310/// Gives back the functions that changed and what changed in them, which is what the pipeline turns
311/// into `-fopt-info` remarks.
312pub fn fold(
313    module: &mut Module,
314    names: &mut Interner,
315    no_builtin: &[String],
316    pic: Pic,
317    fuel: &mut Fuel,
318) -> Vec<(FuncId, Stats)> {
319    let shapes = Shapes::of(module, names);
320    // What each symbol was called in the source, for the declarations where the two differ. A call
321    // names a symbol, and a symbol an assembler name replaced says nothing about which library
322    // function it is, so this is what the two names are put back together through.
323    let standard: HashMap<Symbol, Symbol> =
324        module.funcs().filter_map(|id| Some((module[id].name, module[id].spelled?))).collect();
325    // A module that defines one of these names itself is where that function comes from, and what a
326    // function called `fputs` does in there is whatever it was written to do.
327    let defined: HashSet<Symbol> = module
328        .funcs()
329        .filter(|&id| !module[id].is_declaration())
330        .map(|id| module[id].name)
331        .collect();
332    // One table for the module rather than one per function, so that two calls folded to the same
333    // string share one object instead of each getting one of its own.
334    let mut texts: HashMap<Vec<u8>, Symbol> = HashMap::new();
335    let mut done = Vec::new();
336    for id in module.funcs().collect::<Vec<FuncId>>() {
337        // A function with none of these names in it is most of them, and the answer for one is a
338        // walk over its instructions that allocates nothing. The two tables below are a vector and
339        // a predecessor list per block, which is a cost worth not paying over a module whose
340        // functions print nothing.
341        if module[id].is_declaration() || !mentions(&module[id], names, &standard) {
342            continue;
343        }
344        let mut stats = Stats::new();
345        // The whole body is read before any of it changes. A plan names values the body holds, and
346        // working the next one out from a body half rewritten is how a pass comes to read a value
347        // whose definition it has just taken away.
348        let plans = {
349            let func = &module[id];
350            let site = Site {
351                module,
352                func,
353                cfg: &Cfg::new(func),
354                shapes: &shapes,
355                counts: &uses::count(func),
356                defined: &defined,
357                standard: &standard,
358                names,
359                no_builtin,
360                pic,
361            };
362            site.survey(fuel, &mut stats)
363        };
364        for (inst, plan) in plans {
365            apply(module, id, names, &mut texts, inst, plan);
366        }
367        if stats.changed() {
368            done.push((id, stats));
369        }
370    }
371    done
372}
373
374/// Whether this function calls any of the names a fold reads.
375fn mentions(func: &Func, names: &Interner, standard: &HashMap<Symbol, Symbol>) -> bool {
376    func.blocks().flat_map(|block| func.insts(block)).any(|inst| {
377        let data = &func[inst];
378        let Extra::Call(at) = data.extra else { return false };
379        data.opcode == Opcode::Call
380            && func[at].callee.is_some_and(|callee| {
381                let spelled = standard.get(&callee).copied().unwrap_or(callee);
382                SOURCES.contains(&names.resolve(spelled))
383            })
384    })
385}
386
387/// One function and everything reading it takes to answer what a call in it writes.
388struct Site<'a> {
389    /// The module it is in, which is where a string literal's bytes are.
390    module: &'a Module,
391    /// The function.
392    func: &'a Func,
393    /// Its shape, which is what a block parameter's arguments are found through.
394    cfg: &'a Cfg,
395    /// What the module allows a replacement call to look like.
396    shapes: &'a Shapes,
397    /// How many times each value is read, which is what says a result is ignored.
398    counts: &'a [u32],
399    /// The names this module defines bodies for.
400    defined: &'a HashSet<Symbol>,
401    /// What each renamed symbol was called in the source.
402    standard: &'a HashMap<Symbol, Symbol>,
403    /// The spellings, for reading a callee's name.
404    names: &'a Interner,
405    /// The names `-fno-builtin-<name>` took away.
406    no_builtin: &'a [String],
407    /// Which definitions something else may replace at load time.
408    pic: Pic,
409}
410
411impl Site<'_> {
412    /// Every call in this function that has a plan, with the plan.
413    fn survey(&self, fuel: &mut Fuel, stats: &mut Stats) -> Vec<(Inst, Plan)> {
414        let mut plans = Vec::new();
415        for block in self.func.blocks().collect::<Vec<_>>() {
416            for inst in self.func.insts(block).collect::<Vec<Inst>>() {
417                let Some(plan) = self.plan(inst) else { continue };
418                if !fuel.take() {
419                    stats.missed("call to the library folded");
420                    continue;
421                }
422                stats.optimized(match &plan {
423                    Plan::Drop => "call to the library that writes nothing removed",
424                    Plan::Answer(_) => "call to the library whose answer is known folded",
425                    Plan::Swap { .. } => "call to the library folded",
426                });
427                plans.push((inst, plan));
428            }
429        }
430        plans
431    }
432
433    /// What this call writes, where it is one of the calls this knows and the answer can be worked
434    /// out.
435    fn plan(&self, inst: Inst) -> Option<Plan> {
436        let data = &self.func[inst];
437        if data.opcode != Opcode::Call || self.func.mem_in(inst).is_some() {
438            return None;
439        }
440        // A program looking at how many characters went out is a program the count matters to, and
441        // no two of the printf family answer the same number. `strstr` is not in that position: its
442        // answer is what the call is for and the fold produces the same one.
443        let ignored = data.results().all(|result| self.counts[result.index()] == 0);
444        let Extra::Call(at) = data.extra else { return None };
445        let callee = self.func[at].callee?;
446        if self.defined.contains(&callee) {
447            return None;
448        }
449        let name = self.names.resolve(self.standard.get(&callee).copied().unwrap_or(callee));
450        if self.no_builtin.iter().any(|it| it == name) {
451            return None;
452        }
453        let args: Vec<Value> = self.func[data.args].to_vec();
454        // The locked and the unlocked spellings take the same arguments and differ only in how far
455        // the fold may go, so they are an arm each with a flag rather than two bodies.
456        match name {
457            "printf" if ignored => self.printf(&args, false),
458            "printf_unlocked" if ignored => self.printf(&args, true),
459            "fprintf" if ignored => self.fprintf(&args, false),
460            "fprintf_unlocked" if ignored => self.fprintf(&args, true),
461            "fputs" if ignored => self.fputs(&args, false),
462            "fputs_unlocked" if ignored => self.fputs(&args, true),
463            "strstr" => self.strstr(data, &args),
464            // `index` and `rindex` are the older spellings of the same two searches, and a
465            // program that wrote one of them is asking for the same answer.
466            "strchr" | "index" => self.strchr(data, &args, Side::First),
467            "strrchr" | "rindex" => self.strchr(data, &args, Side::Last),
468            "memchr" => self.memchr(data, &args),
469            "strlen" => self.strlen(data, &args),
470            "strnlen" => self.strnlen(data, &args),
471            "strcmp" => self.strcmp(data, &args),
472            "strncmp" => self.strncmp(data, &args),
473            "strcspn" => self.span(data, &args, Set::Outside),
474            "strspn" => self.span(data, &args, Set::Inside),
475            "strpbrk" => self.strpbrk(data, &args),
476            _ => None,
477        }
478    }
479
480    /// The type this call's one result has, where it has one and it is an integer.
481    ///
482    /// A declaration of another shape is a function of the program's own, and a number is not what
483    /// it answers.
484    fn answers(&self, data: &InstData) -> Option<Type> {
485        let mut results = data.results();
486        let ty = self.func[results.next()?].ty;
487        (results.next().is_none() && ty.is_int() && !ty.is_vector()).then_some(ty)
488    }
489
490    /// Whether this call's one result is a pointer, which every search for a place gives back.
491    fn places(&self, data: &InstData) -> bool {
492        let mut results = data.results();
493        results.next().is_some_and(|result| self.func[result].ty == Type::PTR)
494            && results.next().is_none()
495    }
496
497    /// The character a search was told to look for, which the call carries as an `int` and the
498    /// library reads as a `char`.
499    fn character(&self, value: Value) -> Option<u8> {
500        let (imm, ty) = crate::fold::constant(self.func, value)?;
501        u8::try_from(imm.signed(ty).rem_euclid(256)).ok()
502    }
503
504    /// A count of bytes the call was given, which has to be a constant that fits a `usize`.
505    ///
506    /// The source writes a small count as an `int` and the call takes a `size_t`, so what the
507    /// argument holds is a widening of the constant rather than the constant, and reading only the
508    /// argument would miss every count anyone actually writes.
509    fn count(&self, value: Value) -> Option<usize> {
510        let narrow = self.widened(value);
511        let (imm, ty) = crate::fold::constant(self.func, narrow)?;
512        // A count the source wrote as a negative number is not a count, whatever the conversion
513        // makes of it, and folding on one would be reading an object that is not there.
514        (narrow == value || imm.signed(ty) >= 0).then_some(())?;
515        usize::try_from(imm.unsigned()).ok()
516    }
517
518    /// What this value is a widening of, or the value itself where it is not one.
519    ///
520    /// Both conversions leave a non negative constant alone, so which one it was only matters for
521    /// refusing a negative one, and the caller is the one that does that.
522    fn widened(&self, value: Value) -> Value {
523        let Def::Result { inst, .. } = self.func[value].def else { return value };
524        if !matches!(self.func[inst].opcode, Opcode::SExt | Opcode::ZExt) {
525            return value;
526        }
527        self.func[self.func[inst].args].first().copied().unwrap_or(value)
528    }
529
530    /// Where a `strchr` or a `strrchr` finds its character.
531    ///
532    /// A terminator is found at the end of the string rather than not at all, which is what makes
533    /// `strchr(s, 0)` the address of the terminator and is the one place the bytes this reads and
534    /// the string it is searching are not the same length.
535    fn strchr(&self, data: &InstData, args: &[Value], side: Side) -> Option<Plan> {
536        if args.len() != 2 || self.func[args[0]].ty != Type::PTR || !self.places(data) {
537            return None;
538        }
539        let wanted = self.character(args[1])?;
540        let Some(text) = self.one(args[0]) else {
541            // A string has one terminator in it, so looking for that one from the right finds the
542            // same place as looking for it from the left, and which end the walk started at stops
543            // mattering. That is an answer even where nothing at all is known about the string.
544            return match (wanted, side) {
545                (0, Side::Last) => {
546                    self.call("strchr", vec![Argument::Have(args[0]), Argument::Char(0)])
547                }
548                _ => None,
549            };
550        };
551        let found = match (wanted, side) {
552            (0, _) => Some(text.len()),
553            (_, Side::First) => text.iter().position(|&byte| byte == wanted),
554            (_, Side::Last) => text.iter().rposition(|&byte| byte == wanted),
555        };
556        Some(Plan::Answer(match found {
557            Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
558            None => Answer::Nowhere,
559        }))
560    }
561
562    /// Where a `memchr` finds its character, which is a search over a count rather than up to a
563    /// terminator.
564    ///
565    /// So this reads the object's bytes rather than the string in it, and it refuses a count the
566    /// object does not have that many bytes for, since a call that reads past the end of what the
567    /// compiler can see is a call whose answer the compiler does not know.
568    fn memchr(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
569        (args.len() == 3).then_some(())?; // not a threshold: `memchr` takes three arguments.
570        if self.func[args[0]].ty != Type::PTR || !self.places(data) {
571            return None;
572        }
573        let wanted = self.character(args[1])?;
574        let count = self.count(args[2])?;
575        let bytes = self.raw(args[0])?;
576        let window = bytes.get(..count)?;
577        Some(Plan::Answer(match window.iter().position(|&byte| byte == wanted) {
578            Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
579            None => Answer::Nowhere,
580        }))
581    }
582
583    /// How long a string this module holds is.
584    fn strlen(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
585        if args.len() != 1 || self.func[args[0]].ty != Type::PTR {
586            return None;
587        }
588        self.answers(data)?;
589        let text = self.one(args[0])?;
590        Some(Plan::Answer(Answer::Number(i128::try_from(text.len()).ok()?)))
591    }
592
593    /// The same, stopping at a count.
594    ///
595    /// A string with no terminator inside the count is the count, and that needs the object's bytes
596    /// rather than the string in it, because there may be no string in it at all.
597    fn strnlen(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
598        if args.len() != 2 || self.func[args[0]].ty != Type::PTR {
599            return None;
600        }
601        self.answers(data)?;
602        let count = self.count(args[1])?;
603        let bytes = self.raw(args[0])?;
604        let window = bytes.get(..count.min(bytes.len()))?;
605        let len = match window.iter().position(|&byte| byte == 0) {
606            Some(at) => at,
607            // Nothing terminated it inside the window, so the answer is the count only where the
608            // window was the whole count.
609            None if window.len() == count => count,
610            None => return None,
611        };
612        Some(Plan::Answer(Answer::Number(i128::try_from(len).ok()?)))
613    }
614
615    /// How two strings this module holds compare.
616    fn strcmp(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
617        (args.len() == 2).then_some(())?;
618        self.compared(data, args, usize::MAX)
619    }
620
621    /// The same over a count the call was given, which has to be a constant.
622    fn strncmp(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
623        (args.len() == 3).then_some(())?; // not a threshold: `strncmp` takes three arguments.
624        let count = self.count(args[2])?;
625        self.compared(data, args, count)
626    }
627
628    /// The comparison both of them are, over however many bytes each is allowed to read.
629    ///
630    /// The sign is what the standard promises and the magnitude is not, so this answers one of
631    /// minus one, zero and one, which is what gcc leaves behind as well.
632    fn compared(&self, data: &InstData, args: &[Value], bound: usize) -> Option<Plan> {
633        if self.func[args[0]].ty != Type::PTR || self.func[args[1]].ty != Type::PTR {
634            return None;
635        }
636        self.answers(data)?;
637        let (mut left, mut right) = (self.one(args[0])?, self.one(args[1])?);
638        // The terminator is part of the comparison, since it is what stops one string before the
639        // other and it is smaller than every byte that could be opposite it.
640        left.push(0);
641        right.push(0);
642        let mut answer = 0;
643        for at in 0..bound.min(left.len()).min(right.len()) {
644            if left[at] != right[at] {
645                answer = if left[at] < right[at] { -1 } else { 1 };
646                break;
647            }
648            if left[at] == 0 {
649                break;
650            }
651        }
652        Some(Plan::Answer(Answer::Number(answer)))
653    }
654
655    /// How far into the first string the second one's characters start, or stop.
656    ///
657    /// `strcspn` walks while the character is outside the set and `strspn` walks while it is
658    /// inside, which is one walk with the test turned round, and the shape rules fall out of it:
659    /// nothing is outside an empty set, so `strspn(s, "")` is zero, and everything is, so
660    /// `strcspn(s, "")` is the length of `s`.
661    fn span(&self, data: &InstData, args: &[Value], set: Set) -> Option<Plan> {
662        if args.len() != 2
663            || self.func[args[0]].ty != Type::PTR
664            || self.func[args[1]].ty != Type::PTR
665        {
666            return None;
667        }
668        let ty = self.answers(data)?;
669        // An empty first string is no bytes to walk over, whatever the set is, and that is the
670        // answer `strcspn("", s)` wants where nothing is known about `s`.
671        if let Some(text) = self.one(args[0])
672            && text.is_empty()
673        {
674            return Some(Plan::Answer(Answer::Number(0)));
675        }
676        let accept = self.one(args[1])?;
677        // Both walks are the same walk with the test turned round, and an empty set needs no arm of
678        // its own here, because nothing is inside one and so the walk stops at once or not at all.
679        if let Some(text) = self.one(args[0]) {
680            let len = text
681                .iter()
682                .position(|byte| accept.contains(byte) != matches!(set, Set::Inside))
683                .unwrap_or(text.len());
684            return Some(Plan::Answer(Answer::Number(i128::try_from(len).ok()?)));
685        }
686        // Nothing is known about the string, so only the shape rules are left.
687        accept.is_empty().then_some(())?;
688        match set {
689            Set::Inside => Some(Plan::Answer(Answer::Number(0))),
690            // A number the call gives back and a number `strlen` gives back have to be the same
691            // width, since what reads the first is going to read the second and nothing here writes
692            // a conversion.
693            Set::Outside => {
694                let (_, signature) = self.shapes.get("strlen")?;
695                signature.return_types().eq([ty]).then_some(())?;
696                self.call("strlen", vec![Argument::Have(args[0])])
697            }
698        }
699    }
700
701    /// Where the first character of one string that is in the other is.
702    fn strpbrk(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
703        if args.len() != 2 || self.func[args[0]].ty != Type::PTR || !self.places(data) {
704            return None;
705        }
706        if self.func[args[1]].ty != Type::PTR {
707            return None;
708        }
709        let accept = self.one(args[1])?;
710        // Nothing is in an empty set, so the search runs off the end of any string at all.
711        if accept.is_empty() {
712            return Some(Plan::Answer(Answer::Nowhere));
713        }
714        match self.one(args[0]) {
715            Some(text) => {
716                Some(Plan::Answer(match text.iter().position(|byte| accept.contains(byte)) {
717                    Some(at) => Answer::Along(args[0], u64::try_from(at).ok()?),
718                    None => Answer::Nowhere,
719                }))
720            }
721            // A set of one character is a search for that character, which is the same fold
722            // `strstr` of a needle of one character gets.
723            None => match accept.as_slice() {
724                [one] => self.call("strchr", vec![Argument::Have(args[0]), Argument::Char(*one)]),
725                _ => None,
726            },
727        }
728    }
729
730    /// Where a `strstr` finds what it was told to look for.
731    ///
732    /// The three folds gcc has for it, and the order matters: two strings this module holds are an
733    /// answer, and a haystack nothing is known about is a search for a character where the needle is
734    /// one character long.
735    fn strstr(&self, data: &InstData, args: &[Value]) -> Option<Plan> {
736        if args.len() != 2 {
737            return None;
738        }
739        let (haystack, needle) = (args[0], args[1]);
740        if self.func[haystack].ty != Type::PTR || self.func[needle].ty != Type::PTR {
741            return None;
742        }
743        // A declaration of another shape is a function of the program's own, and the answer this
744        // produces is a pointer whatever the program said the call gives back.
745        let mut results = data.results();
746        if !results.next().is_some_and(|result| self.func[result].ty == Type::PTR)
747            || results.next().is_some()
748        {
749            return None;
750        }
751        let needle = self.one(needle)?;
752        // The empty string is found at once, wherever it is looked for and whatever is there.
753        if needle.is_empty() {
754            return Some(Plan::Answer(Answer::Along(haystack, 0)));
755        }
756        match self.one(haystack) {
757            Some(hay) => Some(Plan::Answer(match at(&hay, &needle) {
758                Some(found) => Answer::Along(haystack, u64::try_from(found).ok()?),
759                None => Answer::Nowhere,
760            })),
761            // A needle of one character is a search for that character, which is a smaller function
762            // and is worth doing wherever the haystack came from.
763            None => match needle.as_slice() {
764                [one] => self.call("strchr", vec![Argument::Have(haystack), Argument::Char(*one)]),
765                _ => None,
766            },
767        }
768    }
769
770    /// What a `printf` writes.
771    fn printf(&self, args: &[Value], quiet: bool) -> Option<Plan> {
772        let format = self.one(*args.first()?)?;
773        match args.len() {
774            1 => self.plain(&format, None, quiet),
775            2 if format == b"%s\n" && !quiet && self.func[args[1]].ty == Type::PTR => {
776                self.call("puts", vec![Argument::Have(args[1])])
777            }
778            2 if format == b"%c" && !quiet && self.func[args[1]].ty == int() => {
779                self.call("putchar", vec![Argument::Have(args[1])])
780            }
781            // The same question asked again of the argument, because what `printf("%s", p)` writes
782            // is what `printf(p)` writes for a `p` holding no `%`. A `p` that does hold one is left
783            // alone here and folded by gcc, which is a missed fold and not a wrong answer.
784            2 if format == b"%s" => self.plain(&self.one(args[1])?, None, quiet),
785            _ => None,
786        }
787    }
788
789    /// What an `fprintf` writes.
790    fn fprintf(&self, args: &[Value], quiet: bool) -> Option<Plan> {
791        let stream = *args.first()?;
792        if self.func[stream].ty != Type::PTR {
793            return None;
794        }
795        let format = self.one(*args.get(1)?)?;
796        match args.len() {
797            2 => self.plain(&format, Some((args[1], stream)), quiet),
798            3 if format == b"%c" && !quiet && self.func[args[2]].ty == int() => {
799                self.call("fputc", vec![Argument::Have(args[2]), Argument::Have(stream)])
800            }
801            // Whatever is known about the argument, which is the fold `printf` cannot have: this
802            // one holds the stream, so the call it leaves behind is one the program could have
803            // written for itself.
804            3 if format == b"%s" && self.func[args[2]].ty == Type::PTR => {
805                match self.strings(args[2], DEPTH) {
806                    Some(candidates) => self.string(&candidates, args[2], stream, quiet),
807                    None if quiet => None,
808                    None => {
809                        self.call("fputs", vec![Argument::Have(args[2]), Argument::Have(stream)])
810                    }
811                }
812            }
813            _ => None,
814        }
815    }
816
817    /// What an `fputs` writes.
818    fn fputs(&self, args: &[Value], quiet: bool) -> Option<Plan> {
819        if args.len() != 2 {
820            return None;
821        }
822        let (text, stream) = (args[0], args[1]);
823        if self.func[text].ty != Type::PTR || self.func[stream].ty != Type::PTR {
824            return None;
825        }
826        self.string(&self.strings(text, DEPTH)?, text, stream, quiet)
827    }
828
829    /// What a format holding no `%` writes, given the stream to write it to or nothing.
830    ///
831    /// The empty case comes first and is the one an unlocked spelling is allowed, because a call
832    /// writing nothing is removed without naming any function at all.
833    fn plain(&self, format: &[u8], stream: Option<(Value, Value)>, quiet: bool) -> Option<Plan> {
834        if format.is_empty() {
835            return Some(Plan::Drop);
836        }
837        if quiet || format.contains(&b'%') {
838            return None;
839        }
840        match (format, stream) {
841            ([one], Some((_, stream))) => {
842                self.call("fputc", vec![Argument::Char(*one), Argument::Have(stream)])
843            }
844            // Anything longer takes the whole format, since `fwrite` is told how many bytes to
845            // write and does not go looking for a terminator.
846            (_, Some((text, stream))) => self.fwrite(Argument::Have(text), format.len(), stream),
847            ([one], None) => self.call("putchar", vec![Argument::Char(*one)]),
848            // `puts` writes a newline of its own, so what it has to be given is the format without
849            // its last character, and that is a string the module does not hold yet.
850            (_, None) => {
851                let (&last, rest) = format.split_last()?;
852                match last {
853                    b'\n' => self.call("puts", vec![Argument::Text(rest.to_vec())]),
854                    _ => None,
855                }
856            }
857        }
858    }
859
860    /// What writing this string to this stream is, given every string the pointer may point at.
861    ///
862    /// The candidates have to agree on their length, because the length is what decides which call
863    /// this becomes. They need not agree on their contents unless the length is one, where the
864    /// character itself is an argument.
865    fn string(
866        &self,
867        candidates: &[Vec<u8>],
868        text: Value,
869        stream: Value,
870        quiet: bool,
871    ) -> Option<Plan> {
872        let first = candidates.first()?;
873        if candidates.iter().any(|it| it.len() != first.len()) {
874            return None;
875        }
876        if first.is_empty() {
877            return Some(Plan::Drop);
878        }
879        if quiet {
880            return None;
881        }
882        match first.as_slice() {
883            [one] if candidates.iter().all(|it| it[0] == *one) => {
884                self.call("fputc", vec![Argument::Char(*one), Argument::Have(stream)])
885            }
886            // A length of one that two candidates disagree about is an `fwrite` of one byte, since
887            // the byte itself is not a constant here and the pointer is what has to be written.
888            // That is gcc's answer too, and its output for this is a conditional move feeding an
889            // `fwrite` of one.
890            _ => self.fwrite(Argument::Have(text), first.len(), stream),
891        }
892    }
893
894    /// An `fwrite` of that many bytes from that address.
895    fn fwrite(&self, text: Argument, bytes: usize, stream: Value) -> Option<Plan> {
896        let len = u64::try_from(bytes).ok()?;
897        self.call(
898            "fwrite",
899            vec![text, Argument::Count(1), Argument::Count(len), Argument::Have(stream)],
900        )
901    }
902
903    /// A call to that name, or nothing where this module does not allow one.
904    fn call(&self, callee: &'static str, args: Vec<Argument>) -> Option<Plan> {
905        let (callee, signature) = self.shapes.get(callee)?;
906        Some(Plan::Swap { callee, signature, args })
907    }
908
909    /// The one string this value points at, or `None` where there is more than one of them.
910    fn one(&self, value: Value) -> Option<Vec<u8>> {
911        let mut candidates = self.strings(value, DEPTH)?;
912        (candidates.len() == 1).then(|| candidates.pop()).flatten()
913    }
914
915    /// Every string this value may point at, or `None` where any of them is not one this module
916    /// holds.
917    ///
918    /// A block parameter is every argument every branch to that block passes, which is how the
919    /// conditional expression in `builtins/fputs.c` gets a length without anything having turned it
920    /// into a `select` first. `depth` is what stops the walk on a loop, where a parameter's
921    /// argument is the parameter.
922    fn strings(&self, value: Value, depth: u32) -> Option<Vec<Vec<u8>>> {
923        if depth == 0 {
924            return None;
925        }
926        match self.func[value].def {
927            Def::Param { block, index } => {
928                let preds = self.cfg.predecessors(block);
929                if preds.is_empty() {
930                    return None;
931                }
932                let mut all = Vec::new();
933                for &pred in preds {
934                    let term = self.func.terminator(pred)?;
935                    for call in self.func.successors(term).collect::<Vec<_>>() {
936                        if call.block != block {
937                            continue;
938                        }
939                        let arg = *self.func[call.args].get(index as usize)?;
940                        all.extend(self.strings(arg, depth - 1)?);
941                    }
942                }
943                (!all.is_empty()).then_some(all)
944            }
945            Def::Result { inst, .. } if self.func[inst].opcode == Opcode::Select => {
946                let args = &self.func[self.func[inst].args];
947                let (then, other) = (*args.get(1)?, *args.get(2)?);
948                let mut all = self.strings(then, depth - 1)?;
949                all.extend(self.strings(other, depth - 1)?);
950                Some(all)
951            }
952            _ => Some(vec![self.literal(value)?]),
953        }
954    }
955
956    /// The bytes up to the first terminator at the address this value is, where that address is
957    /// inside a read only object this module vouches for.
958    fn literal(&self, value: Value) -> Option<Vec<u8>> {
959        let bytes = self.raw(value)?;
960        let end = bytes.iter().position(|&byte| byte == 0)?;
961        Some(bytes[..end].to_vec())
962    }
963
964    /// Every byte from the address this value is to the end of the object it is in.
965    ///
966    /// The same walk as above with nothing stopping it at a terminator, because `memchr` is told
967    /// how far to read rather than going looking for one, and `strnlen` may be told to stop before
968    /// there is one. A caller that wants a string wants [`Self::literal`] instead.
969    fn raw(&self, value: Value) -> Option<Vec<u8>> {
970        let (base, offset) = self.address(value)?;
971        let Def::Result { inst, .. } = self.func[base].def else { return None };
972        if self.func[inst].opcode != Opcode::GlobalAddr {
973            return None;
974        }
975        let Extra::Symbol(name) = self.func[inst].extra else { return None };
976        let Some(SymbolRef::Global(id)) = self.module.lookup(name) else { return None };
977        let global = &self.module[id];
978        if !global.constant || !vouched(global, self.pic) {
979            return None;
980        }
981        let mut bytes = Vec::new();
982        for &datum in &self.module[global.init?] {
983            match datum {
984                Datum::Bytes(range) => bytes.extend_from_slice(&self.module[range]),
985                Datum::Zero(count) => {
986                    bytes.resize(bytes.len().checked_add(usize::try_from(count).ok()?)?, 0);
987                }
988                // A number written in the target's byte order, or an address the linker has not
989                // filled in. Neither is a byte this can read, and what follows one is at an offset
990                // that is right only if this one's width is, so the walk stops.
991                Datum::Scalar { .. } | Datum::Addr(_) | Datum::Away(_) => return None,
992            }
993        }
994        // An object whose image stops short of its size is zero from there on, which is what an
995        // array with fewer initializers than members is and is a byte a search may reach.
996        let size = usize::try_from(global.size).ok()?;
997        if bytes.len() < size {
998            bytes.resize(size, 0);
999        }
1000        Some(bytes.get(usize::try_from(offset).ok()?..)?.to_vec())
1001    }
1002
1003    /// The address this value is, as something it was computed from and a distance in bytes from
1004    /// it.
1005    ///
1006    /// The same walk [`crate::image`] does down a chain of `ptr_add` of a constant, because an
1007    /// index into a string literal is one of these and the frontend writes one per index.
1008    fn address(&self, mut value: Value) -> Option<(Value, i128)> {
1009        let mut offset: i128 = 0;
1010        for _ in 0..DEPTH {
1011            let Def::Result { inst, .. } = self.func[value].def else {
1012                return Some((value, offset));
1013            };
1014            if self.func[inst].opcode != Opcode::PtrAdd {
1015                return Some((value, offset));
1016            }
1017            let args = &self.func[self.func[inst].args];
1018            offset = offset.checked_add(self.step(*args.get(1)?)?)?;
1019            value = *args.first()?;
1020        }
1021        None
1022    }
1023
1024    /// The constant this value is, looking through a widening of one.
1025    ///
1026    /// An index into an array is an `int` where the source wrote one, and a pointer is sixty four
1027    /// bits, so what the frontend leaves in front of a `ptr_add` is a `sext` of a constant rather
1028    /// than a constant. This runs before anything has folded that, since everything that would is
1029    /// one function at a time and the function pipeline has not started, so the walk above would
1030    /// stop at the first index written in the source without this.
1031    fn step(&self, mut value: Value) -> Option<i128> {
1032        for _ in 0..DEPTH {
1033            if let Some((imm, ty)) = crate::fold::constant(self.func, value) {
1034                return Some(imm.signed(ty));
1035            }
1036            let Def::Result { inst, .. } = self.func[value].def else { return None };
1037            match self.func[inst].opcode {
1038                // The narrow value read the way the widening reads it, which for the signed one is
1039                // the same number and for the unsigned one is the same number only where it was
1040                // not negative.
1041                Opcode::SExt => value = *self.func[self.func[inst].args].first()?,
1042                Opcode::ZExt => {
1043                    let arg = *self.func[self.func[inst].args].first()?;
1044                    let (imm, _) = crate::fold::constant(self.func, arg)?;
1045                    return i128::try_from(imm.unsigned()).ok();
1046                }
1047                _ => return None,
1048            }
1049        }
1050        None
1051    }
1052}
1053
1054/// Writes one plan into the function.
1055fn apply(
1056    module: &mut Module,
1057    id: FuncId,
1058    names: &mut Interner,
1059    texts: &mut HashMap<Vec<u8>, Symbol>,
1060    inst: Inst,
1061    plan: Plan,
1062) {
1063    let (callee, signature, args) = match plan {
1064        Plan::Drop => {
1065            module[id].remove_inst(inst);
1066            return;
1067        }
1068        Plan::Answer(answer) => {
1069            let width = size(module);
1070            answered(&mut module[id], inst, answer, width);
1071            return;
1072        }
1073        Plan::Swap { callee, signature, args } => (callee, signature, args),
1074    };
1075    // The objects first, because a string the fold prints belongs to the module and the module is
1076    // what the function is reached through.
1077    let symbols: Vec<Option<Symbol>> = args
1078        .iter()
1079        .map(|arg| match arg {
1080            Argument::Text(bytes) => Some(object(module, names, texts, bytes)),
1081            _ => None,
1082        })
1083        .collect();
1084    let width = size(module);
1085    let func = &mut module[id];
1086    let span = func.span(inst);
1087    let mut values = Vec::with_capacity(args.len());
1088    for (arg, symbol) in args.iter().zip(symbols) {
1089        values.push(match arg {
1090            Argument::Have(value) => *value,
1091            Argument::Char(byte) => constant(func, inst, int(), i128::from(*byte)),
1092            Argument::Count(count) => constant(func, inst, width, i128::from(*count)),
1093            Argument::Text(_) => {
1094                let extra = Extra::Symbol(symbol.expect("a text argument has an object"));
1095                let data = InstData { extra, ..InstData::new(Opcode::GlobalAddr) };
1096                let made = func.create_inst(data, &[Type::PTR], span);
1097                func.insert_before(made, inst);
1098                func[made].results().next().expect("an address is one value")
1099            }
1100        });
1101    }
1102    let results: Vec<Type> = signature.return_types().collect();
1103    let sig = func.add_signature(signature);
1104    let varargs = func.push_abis(&[]);
1105    let info = func.add_call(CallInfo { callee: Some(callee), signature: sig, varargs });
1106    let args = func.push_values(&values);
1107    let data = InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) };
1108    let made = func.create_inst(data, &results, span);
1109    func.insert_before(made, inst);
1110    // Whoever read the old call's answer reads the new one's, where the two are the same kind of
1111    // thing. The printf family is folded only where nothing read it, so the map is empty there and
1112    // this costs a walk over a function that is about to be walked anyway.
1113    let forward: HashMap<Value, Value> = func[inst]
1114        .results()
1115        .zip(func[made].results().collect::<Vec<Value>>())
1116        .filter(|&(from, to)| func[from].ty == func[to].ty)
1117        .collect();
1118    if !forward.is_empty() {
1119        uses::substitute(func, &forward);
1120    }
1121    func.remove_inst(inst);
1122}
1123
1124/// Writes the answer a search worked out in place of the call that would have worked it out.
1125fn answered(func: &mut Func, inst: Inst, answer: Answer, width: Type) {
1126    let span = func.span(inst);
1127    let value = match answer {
1128        // The haystack itself, which is what a search for the empty string finds and what a search
1129        // that found its needle at the front of one finds. No instruction at all for either.
1130        Answer::Along(haystack, 0) => haystack,
1131        Answer::Along(haystack, by) => {
1132            let step = constant(func, inst, width, i128::from(by));
1133            let args = func.push_values(&[haystack, step]);
1134            let data = InstData { args, ..InstData::new(Opcode::PtrAdd) };
1135            let made = func.create_inst(data, &[Type::PTR], span);
1136            func.insert_before(made, inst);
1137            func[made].results().next().expect("an address is one value")
1138        }
1139        Answer::Nowhere => {
1140            let zero = constant(func, inst, width, 0);
1141            let args = func.push_values(&[zero]);
1142            let data = InstData { args, ..InstData::new(Opcode::IntToPtr) };
1143            let made = func.create_inst(data, &[Type::PTR], span);
1144            func.insert_before(made, inst);
1145            func[made].results().next().expect("a null pointer is one value")
1146        }
1147        Answer::Number(number) => {
1148            let ty = func[inst]
1149                .results()
1150                .next()
1151                .map(|result| func[result].ty)
1152                .expect("a call whose answer is a number has one");
1153            constant(func, inst, ty, number)
1154        }
1155    };
1156    let forward: HashMap<Value, Value> =
1157        func[inst].results().map(|result| (result, value)).collect();
1158    uses::substitute(func, &forward);
1159    func.remove_inst(inst);
1160}
1161
1162/// Where the second string is inside the first, in bytes from its front.
1163fn at(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1164    haystack.windows(needle.len()).position(|window| window == needle)
1165}
1166
1167/// An integer constant of that type, put in front of the call being replaced.
1168fn constant(func: &mut Func, before: Inst, ty: Type, value: i128) -> Value {
1169    let span = func.span(before);
1170    let imm = func.add_imm(Imm::int(value, ty.lane()));
1171    let data = InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) };
1172    let made = func.create_inst(data, &[ty], span);
1173    func.insert_before(made, before);
1174    func[made].results().next().expect("a constant is one value")
1175}
1176
1177/// The read only object holding these bytes and a terminator, making it the first time it is asked
1178/// for.
1179///
1180/// `.Lfold` rather than `.Lstr`, so that this numbering and the frontend's cannot meet, and a
1181/// number past the end of the table in the case where a program has named one of these itself.
1182fn object(
1183    module: &mut Module,
1184    names: &mut Interner,
1185    texts: &mut HashMap<Vec<u8>, Symbol>,
1186    bytes: &[u8],
1187) -> Symbol {
1188    if let Some(&symbol) = texts.get(bytes) {
1189        return symbol;
1190    }
1191    let mut image = bytes.to_vec();
1192    image.push(0);
1193    let mut symbol = names.intern(&format!(".Lfold.{}", texts.len()));
1194    for next in texts.len().. {
1195        if module.lookup(symbol).is_none() {
1196            break;
1197        }
1198        symbol = names.intern(&format!(".Lfold.{}", next + 1));
1199    }
1200    let mut global = Global::new(symbol, image.len() as u64, 1);
1201    global.linkage = Linkage::Internal;
1202    global.constant = true;
1203    let range = module.push_bytes(&image);
1204    global.init = Some(module.push_data(&[Datum::Bytes(range)]));
1205    module.add_global(global);
1206    texts.insert(bytes.to_vec(), symbol);
1207    symbol
1208}
1209
1210#[cfg(test)]
1211mod tests {
1212    use super::*;
1213
1214    /// What every fixture below starts with, which is the target the counts and widths are of.
1215    const HEAD: &str = "\
1216; ModuleID = 't.c'
1217; format 0
1218target triple = \"x86_64-unknown-linux-gnu\"
1219target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1220";
1221
1222    /// The module that text is, folded, printed, and checked by the verifier on the way out.
1223    ///
1224    /// Printing it rather than walking it, because what a reader of one of these tests wants to
1225    /// know is what the function ended up being, and a chain of accessors says that less clearly
1226    /// than the line it produces.
1227    fn folded(body: &str) -> String {
1228        run(body, &[], &mut Fuel::unlimited())
1229    }
1230
1231    /// The same, under whatever `-fno-builtin-<name>` and fuel the test wants.
1232    fn run(body: &str, no_builtin: &[String], fuel: &mut Fuel) -> String {
1233        let mut names = Interner::new();
1234        let text = format!("{HEAD}{body}");
1235        let mut module = rucc_ir::parse(&text, &mut names).expect("the fixture parses");
1236        fold(&mut module, &mut names, no_builtin, Pic::Executable, fuel);
1237        if let Err(errors) = rucc_ir::verify(&module, &names) {
1238            panic!("the fold left invalid IR, {errors:?}\n{}", rucc_ir::print(&module, &names));
1239        }
1240        rucc_ir::print(&module, &names)
1241    }
1242
1243    /// A `printf` of a format holding no `%` and ending in a newline is a `puts` of the rest of it.
1244    ///
1245    /// The rest of it is a string this module did not hold, because "hello world" terminated is not
1246    /// a suffix of "hello world\n" terminated, so the fold has to leave an object behind as well as
1247    /// a call.
1248    #[test]
1249    fn a_format_that_ends_in_a_newline_is_written_by_puts() {
1250        let out = folded(
1251            r#"
1252global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
1253
1254func @printf(ptr, ...) -> i32, linkage(external);
1255
1256func @g(), linkage(external) {
1257block0:
1258    %0 = global_addr @.Lstr.0
1259    %1 = call @printf(%0) : (ptr, ...) -> i32
1260    return
1261}
1262"#,
1263        );
1264        assert!(out.contains("call @puts("), "{out}");
1265        assert!(!out.contains("call @printf("), "{out}");
1266        assert!(out.contains(r#"@.Lfold.0 : bytes 12 = { bytes "hello world\00" }"#), "{out}");
1267    }
1268
1269    /// A one character format is a `putchar` of that character, and an empty one is nothing at all.
1270    #[test]
1271    fn a_short_format_is_written_by_putchar_or_by_nothing() {
1272        let out = folded(
1273            r#"
1274global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
1275global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1276
1277func @printf(ptr, ...) -> i32, linkage(external);
1278
1279func @g(), linkage(external) {
1280block0:
1281    %0 = global_addr @.Lstr.0
1282    %1 = call @printf(%0) : (ptr, ...) -> i32
1283    %2 = global_addr @.Lstr.1
1284    %3 = call @printf(%2) : (ptr, ...) -> i32
1285    return
1286}
1287"#,
1288        );
1289        assert!(out.contains("iconst.i32 120"), "the character is the argument, {out}");
1290        assert!(out.contains("call @putchar("), "{out}");
1291        assert_eq!(out.matches("call @").count(), 1, "the empty one is gone, {out}");
1292    }
1293
1294    /// `printf("%s\n", p)` is `puts(p)` and `printf("%c", c)` is `putchar(c)`, whatever the
1295    /// argument is.
1296    #[test]
1297    fn the_two_formats_that_are_a_call_on_their_own_are_folded_for_any_argument() {
1298        let out = folded(
1299            r#"
1300global @.Lstr.0 : bytes 4 = { bytes "%s\0a\00" }, align 1, linkage(internal), constant
1301global @.Lstr.1 : bytes 3 = { bytes "%c\00" }, align 1, linkage(internal), constant
1302
1303func @printf(ptr, ...) -> i32, linkage(external);
1304
1305func @g(ptr, i32), linkage(external) {
1306block0(%0: ptr, %1: i32):
1307    %2 = global_addr @.Lstr.0
1308    %3 = call @printf(%2, %0) : (ptr, ...) -> i32
1309    %4 = global_addr @.Lstr.1
1310    %5 = call @printf(%4, %1) : (ptr, ...) -> i32
1311    return
1312}
1313"#,
1314        );
1315        assert!(out.contains("call @puts(%0)"), "{out}");
1316        assert!(out.contains("call @putchar(%1)"), "{out}");
1317    }
1318
1319    /// `printf("%s", p)` where `p` is not a string this module holds stays as it is.
1320    ///
1321    /// There is no stream argument to hand to `fputs`, and `stdout` is not a name a compiler may
1322    /// invent. gcc stops in the same place.
1323    #[test]
1324    fn a_string_argument_nothing_is_known_about_is_left_to_printf() {
1325        let out = folded(
1326            r#"
1327global @.Lstr.0 : bytes 3 = { bytes "%s\00" }, align 1, linkage(internal), constant
1328
1329func @printf(ptr, ...) -> i32, linkage(external);
1330
1331func @g(ptr), linkage(external) {
1332block0(%0: ptr):
1333    %1 = global_addr @.Lstr.0
1334    %2 = call @printf(%1, %0) : (ptr, ...) -> i32
1335    return
1336}
1337"#,
1338        );
1339        assert!(out.contains("call @printf("), "{out}");
1340    }
1341
1342    /// The `fprintf` list, which is the `printf` one with a stream in hand.
1343    ///
1344    /// A format holding no `%` is an `fwrite` of the whole of it rather than a `puts` of part of
1345    /// it, since `fwrite` is told how many bytes to write and adds no newline of its own.
1346    #[test]
1347    fn a_stream_takes_the_whole_format_through_fwrite() {
1348        let out = folded(
1349            r#"
1350global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
1351global @.Lstr.1 : bytes 2 = { bytes "q\00" }, align 1, linkage(internal), constant
1352
1353func @fprintf(ptr, ptr, ...) -> i32, linkage(external);
1354
1355func @g(ptr), linkage(external) {
1356block0(%0: ptr):
1357    %1 = global_addr @.Lstr.0
1358    %2 = call @fprintf(%0, %1) : (ptr, ptr, ...) -> i32
1359    %3 = global_addr @.Lstr.1
1360    %4 = call @fprintf(%0, %3) : (ptr, ptr, ...) -> i32
1361    return
1362}
1363"#,
1364        );
1365        assert!(out.contains("call @fwrite("), "{out}");
1366        assert!(out.contains("iconst.i64 12"), "the whole format, newline and all, {out}");
1367        assert!(out.contains("call @fputc("), "{out}");
1368        assert!(!out.contains("call @fprintf("), "{out}");
1369    }
1370
1371    /// `fprintf(s, "%s", p)` is `fputs(p, s)` however little is known about `p`, which is the fold
1372    /// `printf` cannot have.
1373    #[test]
1374    fn a_string_argument_with_a_stream_beside_it_becomes_fputs() {
1375        let out = folded(
1376            r#"
1377global @.Lstr.0 : bytes 3 = { bytes "%s\00" }, align 1, linkage(internal), constant
1378
1379func @fprintf(ptr, ptr, ...) -> i32, linkage(external);
1380
1381func @g(ptr, ptr), linkage(external) {
1382block0(%0: ptr, %1: ptr):
1383    %2 = global_addr @.Lstr.0
1384    %3 = call @fprintf(%0, %2, %1) : (ptr, ptr, ...) -> i32
1385    return
1386}
1387"#,
1388        );
1389        assert!(out.contains("call @fputs(%1, %0)"), "{out}");
1390    }
1391
1392    /// An `fputs` of a string whose length is known is an `fwrite` of that many bytes, one of a
1393    /// single character is an `fputc`, and one of nothing is nothing.
1394    #[test]
1395    fn fputs_of_a_string_this_module_holds_is_folded_by_its_length() {
1396        let out = folded(
1397            r#"
1398global @.Lstr.0 : bytes 7 = { bytes "abcdef\00" }, align 1, linkage(internal), constant
1399global @.Lstr.1 : bytes 2 = { bytes "z\00" }, align 1, linkage(internal), constant
1400global @.Lstr.2 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1401
1402func @fputs(ptr, ptr) -> i32, linkage(external);
1403
1404func @g(ptr), linkage(external) {
1405block0(%0: ptr):
1406    %1 = global_addr @.Lstr.0
1407    %2 = call @fputs(%1, %0) : (ptr, ptr) -> i32
1408    %3 = global_addr @.Lstr.1
1409    %4 = call @fputs(%3, %0) : (ptr, ptr) -> i32
1410    %5 = global_addr @.Lstr.2
1411    %6 = call @fputs(%5, %0) : (ptr, ptr) -> i32
1412    return
1413}
1414"#,
1415        );
1416        assert!(out.contains("call @fwrite("), "{out}");
1417        assert!(out.contains("iconst.i64 6"), "{out}");
1418        assert!(out.contains("iconst.i32 122"), "{out}");
1419        assert!(out.contains("call @fputc("), "{out}");
1420        assert!(!out.contains("call @fputs("), "the empty one is gone too, {out}");
1421    }
1422
1423    /// An index into a string literal is a string as well, which is what `fputs(s1 + 6, s)` is.
1424    ///
1425    /// The index is an `int` widened to the width of a pointer, because that is what the frontend
1426    /// writes for an index the source wrote as one and nothing has folded it yet where this runs.
1427    /// An index landing on the terminator is the empty string, so that call goes altogether.
1428    #[test]
1429    fn an_index_into_a_literal_is_a_string_of_its_own() {
1430        let out = folded(
1431            r#"
1432global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1433
1434func @fputs(ptr, ptr) -> i32, linkage(external);
1435
1436func @g(ptr), linkage(external) {
1437block0(%0: ptr):
1438    %1 = global_addr @.Lstr.0
1439    %2 = iconst.i32 6
1440    %3 = sext.i64 %2
1441    %4 = ptr_add %1, %3
1442    %5 = call @fputs(%4, %0) : (ptr, ptr) -> i32
1443    %6 = iconst.i32 11
1444    %7 = sext.i64 %6
1445    %8 = ptr_add %1, %7
1446    %9 = call @fputs(%8, %0) : (ptr, ptr) -> i32
1447    return
1448}
1449"#,
1450        );
1451        assert!(out.contains("iconst.i64 5"), "world without its terminator, {out}");
1452        assert!(out.contains("call @fwrite("), "{out}");
1453        assert!(!out.contains("call @fputs("), "and the terminator itself is nothing, {out}");
1454    }
1455
1456    /// A conditional expression whose arms are two literals of one length is folded, and one whose
1457    /// arms are two lengths is not.
1458    ///
1459    /// The arms reach the call through a block parameter rather than through a `select`, because
1460    /// that is what the lowering walk builds for a conditional expression, so the walk that answers
1461    /// what string a value is has to go up through the branches to find them.
1462    #[test]
1463    fn a_choice_between_two_literals_is_folded_when_they_are_the_same_length() {
1464        let text = r#"
1465global @.Lstr.0 : bytes 2 = { bytes "f\00" }, align 1, linkage(internal), constant
1466global @.Lstr.1 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
1467global @.Lstr.2 : bytes 4 = { bytes "abc\00" }, align 1, linkage(internal), constant
1468
1469func @fputs(ptr, ptr) -> i32, linkage(external);
1470
1471func @g(ptr, i1), linkage(external) {
1472block0(%0: ptr, %1: i1):
1473    %2 = global_addr @.LEFT
1474    %3 = global_addr @.Lstr.1
1475    br_if %1, block1(%2), block1(%3)
1476block1(%4: ptr):
1477    %5 = call @fputs(%4, %0) : (ptr, ptr) -> i32
1478    return
1479}
1480"#;
1481        let same = folded(&text.replace(".LEFT", ".Lstr.0"));
1482        assert!(same.contains("call @fwrite("), "{same}");
1483        assert!(same.contains("iconst.i64 1"), "{same}");
1484
1485        let differing = folded(&text.replace(".LEFT", ".Lstr.2"));
1486        assert!(differing.contains("call @fputs("), "{differing}");
1487    }
1488
1489    /// A call whose result something reads is left alone.
1490    ///
1491    /// `printf` answers how many characters it wrote and `puts` answers a number that is not that
1492    /// count, so a program looking at the answer is a program this may not touch.
1493    #[test]
1494    fn a_call_whose_answer_is_read_is_not_folded() {
1495        let out = folded(
1496            r#"
1497global @n : bytes 4 = { zero 4 }, align 4, linkage(external)
1498global @.Lstr.0 : bytes 3 = { bytes "a\0a\00" }, align 1, linkage(internal), constant
1499
1500func @printf(ptr, ...) -> i32, linkage(external);
1501
1502func @g(), linkage(external) {
1503block0:
1504    %0 = global_addr @.Lstr.0
1505    %1 = call @printf(%0) : (ptr, ...) -> i32
1506    %2 = global_addr @n
1507    store %1 -> %2, align 4
1508    return
1509}
1510"#,
1511        );
1512        assert!(out.contains("call @printf("), "{out}");
1513    }
1514
1515    /// A module holding the body of its own `printf` means that body.
1516    #[test]
1517    fn a_name_this_module_defines_is_that_definition() {
1518        let out = folded(
1519            r#"
1520global @.Lstr.0 : bytes 3 = { bytes "a\0a\00" }, align 1, linkage(internal), constant
1521
1522func @printf(ptr, ...) -> i32, linkage(external) {
1523block0(%0: ptr):
1524    %1 = iconst.i32 0
1525    return %1
1526}
1527
1528func @g(), linkage(external) {
1529block0:
1530    %0 = global_addr @.Lstr.0
1531    %1 = call @printf(%0) : (ptr, ...) -> i32
1532    return
1533}
1534"#,
1535        );
1536        assert!(out.contains("call @printf("), "{out}");
1537    }
1538
1539    /// A program that declared `puts` as something else keeps the call it had.
1540    ///
1541    /// The verifier holds that a call to a name this module declares carries that name's signature,
1542    /// so the fold either agrees with the declaration or does not happen. A fold that went ahead
1543    /// here would produce IR the compiler itself refuses.
1544    #[test]
1545    fn a_declaration_of_another_shape_stops_the_fold() {
1546        let out = folded(
1547            r#"
1548global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
1549
1550func @printf(ptr, ...) -> i32, linkage(external);
1551func @puts(ptr, i32) -> i32, linkage(external);
1552
1553func @g(), linkage(external) {
1554block0:
1555    %0 = global_addr @.Lstr.0
1556    %1 = call @printf(%0) : (ptr, ...) -> i32
1557    return
1558}
1559"#,
1560        );
1561        assert!(out.contains("call @printf("), "{out}");
1562        assert!(!out.contains("@.Lfold."), "and no object was left behind either, {out}");
1563    }
1564
1565    /// A variable by one of those names stops it as well, since a variable is not a function to
1566    /// call.
1567    #[test]
1568    fn a_variable_by_the_name_of_a_replacement_stops_the_fold() {
1569        let out = folded(
1570            r#"
1571global @putchar : bytes 4 = { zero 4 }, align 4, linkage(external)
1572global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
1573
1574func @printf(ptr, ...) -> i32, linkage(external);
1575
1576func @g(), linkage(external) {
1577block0:
1578    %0 = global_addr @.Lstr.0
1579    %1 = call @printf(%0) : (ptr, ...) -> i32
1580    return
1581}
1582"#,
1583        );
1584        assert!(out.contains("call @printf("), "{out}");
1585    }
1586
1587    /// An `_unlocked` spelling gets the one fold that names no function.
1588    ///
1589    /// A system need not have a `puts_unlocked` for the compiler to name, which is the reason the
1590    /// torture program gives and the place gcc stops too.
1591    #[test]
1592    fn the_unlocked_spellings_are_only_removed_when_they_write_nothing() {
1593        let out = folded(
1594            r#"
1595global @.Lstr.0 : bytes 13 = { bytes "hello world\0a\00" }, align 1, linkage(internal), constant
1596global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1597
1598func @printf_unlocked(ptr, ...) -> i32, linkage(external);
1599
1600func @g(), linkage(external) {
1601block0:
1602    %0 = global_addr @.Lstr.0
1603    %1 = call @printf_unlocked(%0) : (ptr, ...) -> i32
1604    %2 = global_addr @.Lstr.1
1605    %3 = call @printf_unlocked(%2) : (ptr, ...) -> i32
1606    return
1607}
1608"#,
1609        );
1610        assert_eq!(out.matches("call @printf_unlocked(").count(), 1, "{out}");
1611        assert!(!out.contains("call @puts("), "{out}");
1612    }
1613
1614    /// `-fno-builtin-printf` takes one name away and leaves the rest of the family folded.
1615    #[test]
1616    fn one_name_can_be_taken_away_without_taking_the_family_away() {
1617        let body = r#"
1618global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
1619
1620func @printf(ptr, ...) -> i32, linkage(external);
1621func @fputs(ptr, ptr) -> i32, linkage(external);
1622
1623func @g(ptr), linkage(external) {
1624block0(%0: ptr):
1625    %1 = global_addr @.Lstr.0
1626    %2 = call @printf(%1) : (ptr, ...) -> i32
1627    %3 = call @fputs(%1, %0) : (ptr, ptr) -> i32
1628    return
1629}
1630"#;
1631        let out = run(body, &["printf".to_owned()], &mut Fuel::unlimited());
1632        assert!(out.contains("call @printf("), "{out}");
1633        assert!(out.contains("call @fputc("), "and the other one still folded, {out}");
1634    }
1635
1636    /// Fuel stops it, which is what a bisection over a miscompilation needs of every transformation
1637    /// here.
1638    #[test]
1639    fn a_run_out_of_fuel_transforms_nothing() {
1640        let body = r#"
1641global @.Lstr.0 : bytes 2 = { bytes "x\00" }, align 1, linkage(internal), constant
1642
1643func @printf(ptr, ...) -> i32, linkage(external);
1644
1645func @g(), linkage(external) {
1646block0:
1647    %0 = global_addr @.Lstr.0
1648    %1 = call @printf(%0) : (ptr, ...) -> i32
1649    return
1650}
1651"#;
1652        let mut fuel = Fuel::of(0);
1653        let out = run(body, &[], &mut fuel);
1654        assert!(out.contains("call @printf("), "{out}");
1655        assert_eq!(fuel.spent(), 0);
1656    }
1657
1658    /// Two calls folded to the same string share one object rather than getting one each.
1659    #[test]
1660    fn one_object_serves_every_call_that_prints_the_same_thing() {
1661        let out = folded(
1662            r#"
1663global @.Lstr.0 : bytes 4 = { bytes "hi\0a\00" }, align 1, linkage(internal), constant
1664
1665func @printf(ptr, ...) -> i32, linkage(external);
1666
1667func @g(), linkage(external) {
1668block0:
1669    %0 = global_addr @.Lstr.0
1670    %1 = call @printf(%0) : (ptr, ...) -> i32
1671    %2 = call @printf(%0) : (ptr, ...) -> i32
1672    return
1673}
1674
1675func @h(), linkage(external) {
1676block0:
1677    %0 = global_addr @.Lstr.0
1678    %1 = call @printf(%0) : (ptr, ...) -> i32
1679    return
1680}
1681"#,
1682        );
1683        assert_eq!(out.matches("@.Lfold.0 : bytes").count(), 1, "{out}");
1684        assert!(!out.contains("@.Lfold.1"), "{out}");
1685        assert_eq!(out.matches("call @puts(").count(), 3, "{out}");
1686    }
1687
1688    /// A search for the empty string finds it at the front of whatever it was given.
1689    ///
1690    /// The haystack need not be a string this module holds, because the answer does not depend on
1691    /// what is in it.
1692    #[test]
1693    fn a_search_for_nothing_answers_with_the_haystack_itself() {
1694        let out = folded(
1695            r#"
1696global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1697
1698func @strstr(ptr, ptr) -> ptr, linkage(external);
1699
1700func @g(ptr) -> ptr, linkage(external) {
1701block0(%0: ptr):
1702    %1 = global_addr @.Lstr.0
1703    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1704    return %2
1705}
1706"#,
1707        );
1708        assert!(!out.contains("call @strstr("), "{out}");
1709        assert!(out.contains("return %0"), "{out}");
1710    }
1711
1712    /// Two strings this module holds answer themselves, at a place in the first or nowhere in it.
1713    #[test]
1714    fn two_strings_this_module_holds_answer_without_a_call() {
1715        let out = folded(
1716            r#"
1717global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1718global @.Lstr.1 : bytes 2 = { bytes "w\00" }, align 1, linkage(internal), constant
1719global @.Lstr.2 : bytes 3 = { bytes "zz\00" }, align 1, linkage(internal), constant
1720
1721func @strstr(ptr, ptr) -> ptr, linkage(external);
1722func @use(ptr, ptr), linkage(external);
1723
1724func @g(), linkage(external) {
1725block0:
1726    %0 = global_addr @.Lstr.0
1727    %1 = global_addr @.Lstr.1
1728    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1729    %3 = global_addr @.Lstr.2
1730    %4 = call @strstr(%0, %3) : (ptr, ptr) -> ptr
1731    call @use(%2, %4) : (ptr, ptr)
1732    return
1733}
1734"#,
1735        );
1736        assert!(!out.contains("call @strstr("), "{out}");
1737        assert!(out.contains("ptr_add %0, "), "the w is six bytes along, {out}");
1738        assert!(out.contains("iconst.i64 6"), "{out}");
1739        assert!(out.contains("inttoptr"), "and the zz is nowhere in it, {out}");
1740    }
1741
1742    /// A one character needle is a search for a character, which `strchr` is the name of.
1743    #[test]
1744    fn a_one_character_needle_becomes_a_search_for_that_character() {
1745        let out = folded(
1746            r#"
1747global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
1748
1749func @strstr(ptr, ptr) -> ptr, linkage(external);
1750
1751func @g(ptr) -> ptr, linkage(external) {
1752block0(%0: ptr):
1753    %1 = global_addr @.Lstr.0
1754    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1755    return %2
1756}
1757"#,
1758        );
1759        assert!(!out.contains("call @strstr("), "{out}");
1760        assert!(out.contains("call @strchr(%0, "), "{out}");
1761        assert!(out.contains("iconst.i32 111"), "{out}");
1762    }
1763
1764    /// A module whose `strchr` is something else of that name keeps its `strstr` call.
1765    #[test]
1766    fn a_strchr_of_another_shape_is_not_the_one_to_call() {
1767        let out = folded(
1768            r#"
1769global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
1770
1771func @strstr(ptr, ptr) -> ptr, linkage(external);
1772func @strchr(ptr, ptr) -> ptr, linkage(external);
1773
1774func @g(ptr) -> ptr, linkage(external) {
1775block0(%0: ptr):
1776    %1 = global_addr @.Lstr.0
1777    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1778    return %2
1779}
1780"#,
1781        );
1782        assert!(out.contains("call @strstr("), "{out}");
1783    }
1784
1785    /// A declaration renamed by an assembler name is the function the standard describes still.
1786    ///
1787    /// The call names the symbol the rename asked for, and the fold reads the spelling beside it,
1788    /// which is what `gcc.c-torture/execute/builtins/strstr-asm.c` is written to catch.
1789    #[test]
1790    fn a_renamed_declaration_is_still_the_function_it_was_spelled() {
1791        let out = folded(
1792            r#"
1793global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1794
1795func @my_strstr(ptr, ptr) -> ptr, linkage(external), spelled "strstr";
1796
1797func @g(ptr) -> ptr, linkage(external) {
1798block0(%0: ptr):
1799    %1 = global_addr @.Lstr.0
1800    %2 = call @my_strstr(%0, %1) : (ptr, ptr) -> ptr
1801    return %2
1802}
1803"#,
1804        );
1805        assert!(!out.contains("call @my_strstr("), "{out}");
1806        assert!(out.contains("return %0"), "{out}");
1807    }
1808
1809    /// A module that renamed `strchr` gets a call to the symbol it renamed it to.
1810    #[test]
1811    fn a_renamed_replacement_is_called_by_the_symbol_the_rename_asked_for() {
1812        let out = folded(
1813            r#"
1814global @.Lstr.0 : bytes 2 = { bytes "o\00" }, align 1, linkage(internal), constant
1815
1816func @strstr(ptr, ptr) -> ptr, linkage(external);
1817func @my_strchr(ptr, i32) -> ptr, linkage(external), spelled "strchr";
1818
1819func @g(ptr) -> ptr, linkage(external) {
1820block0(%0: ptr):
1821    %1 = global_addr @.Lstr.0
1822    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1823    return %2
1824}
1825"#,
1826        );
1827        assert!(out.contains("call @my_strchr(%0, "), "{out}");
1828        assert!(!out.contains("call @strchr("), "{out}");
1829    }
1830
1831    /// `-fno-builtin-strstr` leaves the call alone.
1832    #[test]
1833    fn a_strstr_taken_away_is_a_call_like_any_other() {
1834        let body = r#"
1835global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
1836
1837func @strstr(ptr, ptr) -> ptr, linkage(external);
1838
1839func @g(ptr) -> ptr, linkage(external) {
1840block0(%0: ptr):
1841    %1 = global_addr @.Lstr.0
1842    %2 = call @strstr(%0, %1) : (ptr, ptr) -> ptr
1843    return %2
1844}
1845"#;
1846        let out = run(body, &["strstr".to_owned()], &mut Fuel::unlimited());
1847        assert!(out.contains("call @strstr("), "{out}");
1848    }
1849
1850    /// `strlen` of a string this module holds is its length, and of a place inside one is the rest.
1851    #[test]
1852    fn strlen_of_a_string_this_module_holds_is_a_number() {
1853        let out = folded(
1854            r#"
1855global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1856
1857func @strlen(ptr) -> i64, linkage(external);
1858func @use(i64, i64), linkage(external);
1859
1860func @g(), linkage(external) {
1861block0:
1862    %0 = global_addr @.Lstr.0
1863    %1 = call @strlen(%0) : (ptr) -> i64
1864    %2 = iconst.i64 6
1865    %3 = ptr_add %0, %2
1866    %4 = call @strlen(%3) : (ptr) -> i64
1867    call @use(%1, %4) : (i64, i64)
1868    return
1869}
1870"#,
1871        );
1872        assert!(!out.contains("call @strlen("), "{out}");
1873        assert!(out.contains("iconst.i64 11"), "{out}");
1874        assert!(out.contains("iconst.i64 5"), "the world on its own, {out}");
1875    }
1876
1877    /// `strnlen` stops at its count, and the count is what the answer is where nothing terminated
1878    /// the string inside it.
1879    #[test]
1880    fn strnlen_answers_the_count_where_the_string_runs_past_it() {
1881        let out = folded(
1882            r#"
1883global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1884
1885func @strnlen(ptr, i64) -> i64, linkage(external);
1886func @use(i64, i64), linkage(external);
1887
1888func @g(), linkage(external) {
1889block0:
1890    %0 = global_addr @.Lstr.0
1891    %1 = iconst.i64 3
1892    %2 = call @strnlen(%0, %1) : (ptr, i64) -> i64
1893    %3 = iconst.i64 40
1894    %4 = call @strnlen(%0, %3) : (ptr, i64) -> i64
1895    call @use(%2, %4) : (i64, i64)
1896    return
1897}
1898"#,
1899        );
1900        assert!(!out.contains("call @strnlen("), "{out}");
1901        assert!(out.contains("iconst.i64 3"), "the count came first, {out}");
1902        assert!(out.contains("iconst.i64 11"), "the terminator came first, {out}");
1903    }
1904
1905    /// A count the source wrote as an `int` reaches the call widened, and the constant is under the
1906    /// widening rather than in the argument.
1907    #[test]
1908    fn a_count_that_was_widened_on_the_way_in_is_still_a_count() {
1909        let out = folded(
1910            r#"
1911global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1912
1913func @strnlen(ptr, i64) -> i64, linkage(external);
1914
1915func @g() -> i64, linkage(external) {
1916block0:
1917    %0 = global_addr @.Lstr.0
1918    %1 = iconst.i32 4
1919    %2 = sext.i64 %1
1920    %3 = call @strnlen(%0, %2) : (ptr, i64) -> i64
1921    return %3
1922}
1923"#,
1924        );
1925        assert!(!out.contains("call @strnlen("), "{out}");
1926        assert!(out.contains("iconst.i64 4"), "{out}");
1927    }
1928
1929    /// `memchr` reads a count rather than a string, so it finds a byte past the terminator, and it
1930    /// answers nowhere where the byte is outside the count.
1931    #[test]
1932    fn memchr_searches_the_object_rather_than_the_string_in_it() {
1933        let out = folded(
1934            r#"
1935global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1936
1937func @memchr(ptr, i32, i64) -> ptr, linkage(external);
1938func @use(ptr, ptr), linkage(external);
1939
1940func @g(), linkage(external) {
1941block0:
1942    %0 = global_addr @.Lstr.0
1943    %1 = iconst.i32 0
1944    %2 = iconst.i64 12
1945    %3 = call @memchr(%0, %1, %2) : (ptr, i32, i64) -> ptr
1946    %4 = iconst.i32 100
1947    %5 = iconst.i64 10
1948    %6 = call @memchr(%0, %4, %5) : (ptr, i32, i64) -> ptr
1949    call @use(%3, %6) : (ptr, ptr)
1950    return
1951}
1952"#,
1953        );
1954        assert!(!out.contains("call @memchr("), "{out}");
1955        assert!(out.contains("iconst.i64 11"), "the terminator is inside the count, {out}");
1956        assert!(out.contains("inttoptr.ptr "), "the d is one byte past the count, {out}");
1957    }
1958
1959    /// A count the object does not have that many bytes for is a read the compiler cannot see the
1960    /// end of, so the call stays.
1961    #[test]
1962    fn a_memchr_that_runs_off_the_object_is_left_alone() {
1963        let out = folded(
1964            r#"
1965global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1966
1967func @memchr(ptr, i32, i64) -> ptr, linkage(external);
1968
1969func @g() -> ptr, linkage(external) {
1970block0:
1971    %0 = global_addr @.Lstr.0
1972    %1 = iconst.i32 122
1973    %2 = iconst.i64 13
1974    %3 = call @memchr(%0, %1, %2) : (ptr, i32, i64) -> ptr
1975    return %3
1976}
1977"#,
1978        );
1979        assert!(out.contains("call @memchr("), "{out}");
1980    }
1981
1982    /// `strchr` finds the first, `strrchr` the last, and a search for the terminator finds it at the
1983    /// end rather than not at all.
1984    #[test]
1985    fn the_two_character_searches_answer_from_either_end() {
1986        let out = folded(
1987            r#"
1988global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
1989
1990func @strchr(ptr, i32) -> ptr, linkage(external);
1991func @strrchr(ptr, i32) -> ptr, linkage(external);
1992func @use(ptr, ptr, ptr, ptr), linkage(external);
1993
1994func @g(), linkage(external) {
1995block0:
1996    %0 = global_addr @.Lstr.0
1997    %1 = iconst.i32 111
1998    %2 = call @strchr(%0, %1) : (ptr, i32) -> ptr
1999    %3 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
2000    %4 = iconst.i32 0
2001    %5 = call @strchr(%0, %4) : (ptr, i32) -> ptr
2002    %6 = iconst.i32 122
2003    %7 = call @strchr(%0, %6) : (ptr, i32) -> ptr
2004    call @use(%2, %3, %5, %7) : (ptr, ptr, ptr, ptr)
2005    return
2006}
2007"#,
2008        );
2009        assert!(!out.contains("call @strchr("), "{out}");
2010        assert!(!out.contains("call @strrchr("), "{out}");
2011        assert!(out.contains("iconst.i64 4"), "the first o, {out}");
2012        assert!(out.contains("iconst.i64 7"), "the last o, {out}");
2013        assert!(out.contains("iconst.i64 11"), "the terminator, {out}");
2014        assert!(out.contains("inttoptr.ptr "), "there is no z in it, {out}");
2015    }
2016
2017    /// The two comparisons answer one of minus one, zero and one, which is the sign the standard
2018    /// promises and nothing more.
2019    #[test]
2020    fn the_two_comparisons_answer_a_sign() {
2021        let out = folded(
2022            r#"
2023global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2024global @.Lstr.1 : bytes 6 = { bytes "hello\00" }, align 1, linkage(internal), constant
2025
2026func @strcmp(ptr, ptr) -> i32, linkage(external);
2027func @strncmp(ptr, ptr, i64) -> i32, linkage(external);
2028func @use(i32, i32, i32), linkage(external);
2029
2030func @g(), linkage(external) {
2031block0:
2032    %0 = global_addr @.Lstr.0
2033    %1 = global_addr @.Lstr.1
2034    %2 = call @strcmp(%0, %1) : (ptr, ptr) -> i32
2035    %3 = call @strcmp(%1, %0) : (ptr, ptr) -> i32
2036    %4 = iconst.i64 5
2037    %5 = call @strncmp(%0, %1, %4) : (ptr, ptr, i64) -> i32
2038    call @use(%2, %3, %5) : (i32, i32, i32)
2039    return
2040}
2041"#,
2042        );
2043        assert!(!out.contains("call @strcmp("), "{out}");
2044        assert!(!out.contains("call @strncmp("), "{out}");
2045        assert!(out.contains("iconst.i32 1"), "the longer one is the greater, {out}");
2046        assert!(out.contains("iconst.i32 -1"), "and the other way round, {out}");
2047        assert!(out.contains("iconst.i32 0"), "five bytes of each are the same, {out}");
2048    }
2049
2050    /// The two spans walk the same string with the test turned round.
2051    #[test]
2052    fn the_two_spans_are_one_walk_each_way() {
2053        let out = folded(
2054            r#"
2055global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2056global @.Lstr.1 : bytes 4 = { bytes "hel\00" }, align 1, linkage(internal), constant
2057global @.Lstr.2 : bytes 3 = { bytes "wz\00" }, align 1, linkage(internal), constant
2058
2059func @strspn(ptr, ptr) -> i64, linkage(external);
2060func @strcspn(ptr, ptr) -> i64, linkage(external);
2061func @use(i64, i64), linkage(external);
2062
2063func @g(), linkage(external) {
2064block0:
2065    %0 = global_addr @.Lstr.0
2066    %1 = global_addr @.Lstr.1
2067    %2 = call @strspn(%0, %1) : (ptr, ptr) -> i64
2068    %3 = global_addr @.Lstr.2
2069    %4 = call @strcspn(%0, %3) : (ptr, ptr) -> i64
2070    call @use(%2, %4) : (i64, i64)
2071    return
2072}
2073"#,
2074        );
2075        assert!(!out.contains("call @strspn("), "{out}");
2076        assert!(!out.contains("call @strcspn("), "{out}");
2077        assert!(out.contains("iconst.i64 4"), "hello stops at the o, {out}");
2078        assert!(out.contains("iconst.i64 6"), "the w is six bytes along, {out}");
2079    }
2080
2081    /// Nothing is inside an empty set, so `strspn(s, "")` is zero and `strcspn(s, "")` is the length
2082    /// of `s`, whatever `s` is.
2083    #[test]
2084    fn an_empty_set_is_a_span_of_nothing_or_of_all_of_it() {
2085        let out = folded(
2086            r#"
2087global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2088
2089func @strspn(ptr, ptr) -> i64, linkage(external);
2090func @strcspn(ptr, ptr) -> i64, linkage(external);
2091func @strlen(ptr) -> i64, linkage(external);
2092func @use(i64, i64), linkage(external);
2093
2094func @g(ptr), linkage(external) {
2095block0(%0: ptr):
2096    %1 = global_addr @.Lstr.0
2097    %2 = call @strspn(%0, %1) : (ptr, ptr) -> i64
2098    %3 = call @strcspn(%0, %1) : (ptr, ptr) -> i64
2099    call @use(%2, %3) : (i64, i64)
2100    return
2101}
2102"#,
2103        );
2104        assert!(!out.contains("call @strspn("), "{out}");
2105        assert!(!out.contains("call @strcspn("), "{out}");
2106        assert!(out.contains("call @strlen(%0)"), "{out}");
2107        assert!(out.contains("iconst.i64 0"), "{out}");
2108    }
2109
2110    /// A `strcspn` answering a width `strlen` does not answer is a fold that would leave a reader
2111    /// holding a number of the wrong size, so it does not happen.
2112    #[test]
2113    fn a_strcspn_of_another_width_than_strlen_is_left_alone() {
2114        let out = folded(
2115            r#"
2116global @.Lstr.0 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2117
2118func @strcspn(ptr, ptr) -> i32, linkage(external);
2119func @strlen(ptr) -> i64, linkage(external);
2120
2121func @g(ptr) -> i32, linkage(external) {
2122block0(%0: ptr):
2123    %1 = global_addr @.Lstr.0
2124    %2 = call @strcspn(%0, %1) : (ptr, ptr) -> i32
2125    return %2
2126}
2127"#,
2128        );
2129        assert!(out.contains("call @strcspn("), "{out}");
2130    }
2131
2132    /// `strpbrk` of a set of one character is a search for that character, and of an empty set is
2133    /// nowhere at all.
2134    #[test]
2135    fn strpbrk_of_a_short_set_is_a_search_or_an_answer() {
2136        let out = folded(
2137            r#"
2138global @.Lstr.0 : bytes 2 = { bytes "w\00" }, align 1, linkage(internal), constant
2139global @.Lstr.1 : bytes 1 = { bytes "\00" }, align 1, linkage(internal), constant
2140
2141func @strpbrk(ptr, ptr) -> ptr, linkage(external);
2142func @strchr(ptr, i32) -> ptr, linkage(external);
2143func @use(ptr, ptr), linkage(external);
2144
2145func @g(ptr), linkage(external) {
2146block0(%0: ptr):
2147    %1 = global_addr @.Lstr.0
2148    %2 = call @strpbrk(%0, %1) : (ptr, ptr) -> ptr
2149    %3 = global_addr @.Lstr.1
2150    %4 = call @strpbrk(%0, %3) : (ptr, ptr) -> ptr
2151    call @use(%2, %4) : (ptr, ptr)
2152    return
2153}
2154"#,
2155        );
2156        assert!(!out.contains("call @strpbrk("), "{out}");
2157        assert!(out.contains("call @strchr(%0, "), "{out}");
2158        assert!(out.contains("iconst.i32 119"), "{out}");
2159        assert!(out.contains("inttoptr.ptr "), "the empty set is nowhere, {out}");
2160    }
2161
2162    /// Two strings this module holds answer `strpbrk` without either call.
2163    #[test]
2164    fn strpbrk_over_two_strings_this_module_holds_is_a_place() {
2165        let out = folded(
2166            r#"
2167global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2168global @.Lstr.1 : bytes 3 = { bytes "wz\00" }, align 1, linkage(internal), constant
2169global @.Lstr.2 : bytes 3 = { bytes "qz\00" }, align 1, linkage(internal), constant
2170
2171func @strpbrk(ptr, ptr) -> ptr, linkage(external);
2172func @use(ptr, ptr), linkage(external);
2173
2174func @g(), linkage(external) {
2175block0:
2176    %0 = global_addr @.Lstr.0
2177    %1 = global_addr @.Lstr.1
2178    %2 = call @strpbrk(%0, %1) : (ptr, ptr) -> ptr
2179    %3 = global_addr @.Lstr.2
2180    %4 = call @strpbrk(%0, %3) : (ptr, ptr) -> ptr
2181    call @use(%2, %4) : (ptr, ptr)
2182    return
2183}
2184"#,
2185        );
2186        assert!(!out.contains("call @strpbrk("), "{out}");
2187        assert!(out.contains("iconst.i64 6"), "the w is six bytes along, {out}");
2188        assert!(out.contains("inttoptr.ptr "), "there is neither a q nor a z in it, {out}");
2189    }
2190
2191    /// `index` and `rindex` are the same two searches under their older names.
2192    #[test]
2193    fn the_older_spellings_of_the_two_searches_are_folded_as_well() {
2194        let out = folded(
2195            r#"
2196global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2197
2198func @index(ptr, i32) -> ptr, linkage(external);
2199func @rindex(ptr, i32) -> ptr, linkage(external);
2200func @use(ptr, ptr), linkage(external);
2201
2202func @g(), linkage(external) {
2203block0:
2204    %0 = global_addr @.Lstr.0
2205    %1 = iconst.i32 111
2206    %2 = call @index(%0, %1) : (ptr, i32) -> ptr
2207    %3 = call @rindex(%0, %1) : (ptr, i32) -> ptr
2208    call @use(%2, %3) : (ptr, ptr)
2209    return
2210}
2211"#,
2212        );
2213        assert!(!out.contains("call @index("), "{out}");
2214        assert!(!out.contains("call @rindex("), "{out}");
2215        assert!(out.contains("iconst.i64 4"), "the first o, {out}");
2216        assert!(out.contains("iconst.i64 7"), "the last o, {out}");
2217    }
2218
2219    /// A search from the right for the terminator is a search from the left for it, because a
2220    /// string has one terminator and both walks find that one.
2221    #[test]
2222    fn a_strrchr_of_the_terminator_is_a_strchr_of_it() {
2223        let out = folded(
2224            r#"
2225func @strrchr(ptr, i32) -> ptr, linkage(external);
2226
2227func @g(ptr) -> ptr, linkage(external) {
2228block0(%0: ptr):
2229    %1 = iconst.i32 0
2230    %2 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
2231    return %2
2232}
2233"#,
2234        );
2235        assert!(!out.contains("call @strrchr("), "{out}");
2236        assert!(out.contains("call @strchr(%0, "), "{out}");
2237    }
2238
2239    /// A search from the right for anything else needs the string, since where the last one is
2240    /// depends on what is in it.
2241    #[test]
2242    fn a_strrchr_of_another_character_needs_the_string() {
2243        let out = folded(
2244            r#"
2245func @strrchr(ptr, i32) -> ptr, linkage(external);
2246
2247func @g(ptr) -> ptr, linkage(external) {
2248block0(%0: ptr):
2249    %1 = iconst.i32 111
2250    %2 = call @strrchr(%0, %1) : (ptr, i32) -> ptr
2251    return %2
2252}
2253"#,
2254        );
2255        assert!(out.contains("call @strrchr("), "{out}");
2256    }
2257
2258    /// A declaration of the wrong shape is a function of the program's own, whatever it is called.
2259    #[test]
2260    fn a_strlen_that_answers_nothing_is_not_the_one_the_library_has() {
2261        let out = folded(
2262            r#"
2263global @.Lstr.0 : bytes 12 = { bytes "hello world\00" }, align 1, linkage(internal), constant
2264
2265func @strlen(ptr), linkage(external);
2266
2267func @g(), linkage(external) {
2268block0:
2269    %0 = global_addr @.Lstr.0
2270    call @strlen(%0) : (ptr)
2271    return
2272}
2273"#,
2274        );
2275        assert!(out.contains("call @strlen("), "{out}");
2276    }
2277}