Skip to main content

rucc_codegen/
abi.rs

1//! Where a function's arguments already are when it starts running, and where a call puts its own.
2//!
3//! Design: `spec/12-abi-and-runtime.md`.
4//!
5//! This is the one part of the calling convention that is not a lowering rule, and it is worth
6//! saying why, because everything else in this crate is. A rule matches a term and rewrites it,
7//! and which register the third argument arrives in is not a fact about any term: it depends on
8//! the argument's position and on the classification of every argument before it. A pattern has
9//! nowhere to put that. So the arguments are built here, by hand, out of what the convention
10//! says, the same way [`crate::finish`] builds a prologue.
11//!
12//! The classification itself is not here either. `rucc-lower` has already run it by the time a
13//! function reaches this crate, which is why the parameters read here are nearly all plain
14//! scalars: an aggregate has been split into the pieces it travels in, and a return through memory
15//! is an ordinary pointer parameter in front of the rest. What is left for this is the step after
16//! classification, from how a value travels to which register it is actually in, which is
17//! [`rucc_target::Places`].
18//!
19//! The one parameter that is not a scalar is an aggregate the classification put in the argument
20//! area whole, which is [`rucc_ir::Abi::ByVal`]. The IR calls it a pointer, because a pointer is
21//! what an instruction reading it has to have, and the convention says the bytes travel and the
22//! pointer does not. So this is the one place that reads what the classification said rather than
23//! only the type, on both sides of the call, and the two sides are the two halves of one copy.
24//!
25//! # What it writes
26//!
27//! One `x64.arg_val_*` per parameter that arrived in a register, at the top of the entry block,
28//! each defining a fresh register constrained to the one the argument arrived in. They encode to
29//! nothing. The point of them is that a parameter has to be defined somewhere for the allocator to
30//! have anything to move, and the entry block cannot define it as a block parameter: there is no
31//! edge into the entry block for the move to go on, which is what `rucc_regalloc::rewrite` asserts.
32//!
33//! What the allocator does with them is the whole of the argument sequence. A parameter that is
34//! read where it arrived costs nothing, and one that is not gets a copy, which is the same
35//! bargain the return already makes and is decided by the same code.
36//!
37//! A parameter whose bytes travelled is the exception to all of that. Its bytes are already in
38//! this function, at a place in the caller's argument area the same walk gives, so nothing is
39//! brought in at all: what the parameter is is where they are, and that is one `lea`. It waits on
40//! the frame the way the loads below it do, and for the same reason.
41//!
42//! A parameter past the last register arrived in the caller's memory rather than in a register, so
43//! it is a load and not a pseudo, and it is a real instruction that encodes to real bytes. How far
44//! up the caller's argument area it is is a number [`rucc_target::Places`] answers here, but where
45//! that area is from inside this function is a distance into a frame, and no frame exists until
46//! after allocation. So the load is written with nothing in its displacement, which of the two
47//! registers it reads through is left to be settled too, and both are filled in by [`crate::finish`]
48//! out of [`crate::frame::Frame::incoming`]. That is the same bargain an `alloca` already makes,
49//! for the same reason and in the same two places.
50//!
51//! # A call
52//!
53//! The same reasoning the other way round, and one instruction rather than several. `x64.call`
54//! and `x64.call_reg` are the only opcodes in the description whose operand vector is empty
55//! there, because nothing about a call's operands is the same from one call to the next, so they
56//! are built here: one read per argument constrained to the register the convention passes it in,
57//! one definition for the value that comes back constrained to the register it comes back in, and
58//! one definition per register the convention does not preserve.
59//!
60//! A call through an address has one operand more, which is the address, and it is the one
61//! operand of a call that is a fact about the instruction rather than about the signature. It
62//! goes in front of the arguments, because the assembler has to find it and an index into a
63//! vector whose length depends on the convention is not a way of finding anything.
64//!
65//! Those last ones are the clobbers, and they are the whole of what the allocator has to know
66//! about a call besides where the values go. Each is a definition of the physical register itself
67//! rather than of a value, since there is no value: it says the register is written here, which
68//! is exactly what stops the allocator from leaving something in one across the call. A register
69//! an argument or the result already names is not repeated, because naming it once already blocks
70//! it for the length of the instruction, which is all a clobber does.
71//!
72//! An argument past the last register the convention has for it is a store into the outgoing area
73//! rather than an operand of the call, written in front of the call in the same block. Where that
74//! area is does not have to wait for the frame the way the incoming one does, because the outgoing
75//! area is at the bottom of the frame and the bottom of the frame is where the stack pointer is:
76//! that is the whole reason the frame puts it there, since it is where the callee will look. So the
77//! offset [`rucc_target::Places`] gives back is the offset the store is written with.
78//!
79//! An object passed by value in memory is the same thing again and a copy rather than a store. The
80//! caller owes the callee a copy it is free to write to, which is what makes a C call by value
81//! different from passing a pointer the callee must not keep, and the argument area is where the
82//! convention says that copy goes. So the bytes are read out of the object and written into the
83//! area a word at a time, in front of the call, with the words chosen by the same function that
84//! chooses them for a `memcpy`. An object with more words than that unrolls to is copied by a call
85//! to the runtime's `memcpy` instead, written in front of the outer call in the same block.
86//!
87//! That is one call built in the middle of building another, which sounds worse than it is.
88//! Nothing of the outer call is in a physical register when the copy is written: every argument
89//! that travels in one is still a virtual register, and the register it has to end up in is a
90//! constraint on an operand of the call instruction, which is not built until every copy in front
91//! of it has been. So what the inner call destroys is what any call destroys, and the allocator
92//! keeps the outer call's values out of those registers the same way it does across a call the
93//! program wrote. The arguments already stored into the outgoing area are safe for a plainer
94//! reason: the inner call's own frame is below the stack pointer and that area is above it.
95//!
96//! The call still reports how many bytes it needed, because the frame reserves as many as the
97//! widest call in the function asked for and cannot know that until every call has been seen.
98
99use rucc_base::{Interner, Symbol};
100use rucc_diag::Span;
101use rucc_ir::{Abi, Drains, Param, Type};
102use rucc_mir as mir;
103use rucc_target::{CallRegs, Constraint, PhysReg, Places, RegClass, Variadic, Where};
104
105use crate::capability;
106use crate::varargs::Area;
107
108pub mod aarch64;
109
110/// Why a parameter could not be brought in.
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum Missing {
113    /// It travels on the x87 stack, which is a `long double` and nothing else. That stack is a
114    /// third register file, it is not one the allocator has, and no instruction in the
115    /// description touches it.
116    OnX87,
117    /// It is a width no pseudo covers, which is anything a machine register does not hold.
118    Width,
119    /// It is a value that comes back in more registers than the convention returns in. A structure
120    /// of at most sixteen bytes comes back in up to two, which is as many as SysV has, and a
121    /// convention with fewer of them returns such a structure through a hidden pointer instead. So
122    /// this is what a signature the classification did not produce would get.
123    NoRoom,
124    /// It is an object whose bytes travel in the argument area and there are more of them than
125    /// any count of them can be written down as. A copy too long to unroll is a call to the
126    /// runtime and not a refusal, so what is left here is an object of two gigabytes or more,
127    /// which is a size the immediate holding the byte count has nowhere to put and a structure no
128    /// program passes.
129    TooBig,
130}
131
132impl Missing {
133    /// What it says when a function could not be compiled because of it.
134    ///
135    /// Worded so that it reads the same about a value arriving and a value being passed, since
136    /// the two are the same fact seen from the two ends of one call.
137    #[must_use]
138    pub fn why(self) -> &'static str {
139        match self {
140            Missing::OnX87 => "is on the x87 stack",
141            Missing::Width => "is a width no argument register holds",
142            Missing::NoRoom => "takes more registers than this convention has for it",
143            Missing::TooBig => "is more bytes than a count of them can be written down as",
144        }
145    }
146}
147
148/// Which register file a value of that type travels in.
149///
150/// The whole of what the two files mean to this module. A float is in the vector one and
151/// everything else is in the general purpose one, which is what both of this machine's conventions
152/// say. A `long double` is in neither and what its register holds is an address, which is a general
153/// purpose value like every other address, so it answers with the other file rather than with the
154/// one its type would suggest.
155fn class_of(ty: Type, conv: &CallRegs) -> RegClass {
156    if ty.is_float() && !on_the_stack(ty) { conv.sse_class } else { conv.int_class }
157}
158
159/// How many bytes of the argument area a value in the vector file takes.
160///
161/// Its own width, lanes included, which is what [`Places::float`] wants and is a number that only
162/// matters to a value the registers ran out before. Everything the machine computes in is a word
163/// or narrower and takes a word either way. The `_Float128` is the one that is not: two words, and
164/// aligned to two words, which is the difference between the argument behind it being placed after
165/// it and being placed on top of half of it.
166pub(crate) fn float_bytes(ty: Type) -> u32 {
167    ty.bits().div_ceil(8).saturating_mul(ty.lanes())
168}
169
170/// Spends the registers an object in the argument area said nothing after it may have.
171pub(crate) fn drain(places: &mut Places<'_>, drains: Drains) {
172    match drains {
173        Drains::Nothing => {}
174        Drains::Integers => places.drain_integers(),
175        Drains::Floats => places.drain_floats(),
176    }
177}
178
179/// How many bytes of the argument area a value in the general purpose file takes.
180///
181/// Its own width, which only a convention that packs the argument area reads, since everywhere
182/// else anything this narrow takes a word. A pointer has no width of its own in the IR, so it is
183/// the word.
184pub(crate) fn int_bytes(ty: Type, conv: &CallRegs) -> u32 {
185    if ty.is_ptr() { conv.word } else { ty.bits().saturating_mul(ty.lanes()).div_ceil(8).max(1) }
186}
187
188/// Whether a value of that type travels as bytes in the argument area because of what it is.
189///
190/// One type does, and it is the `long double`. SysV classifies it X87 and X87UP, which is the
191/// classification that means memory, so it goes where a structure the classification put in memory
192/// goes and what the two ends pass is the address of the bytes. That is not a decision about
193/// registers running out: a `long double` travels in the argument area when it is the only argument
194/// there is.
195///
196/// Sixteen bytes aligned to sixteen, which is what the psABI says the type takes and is the same
197/// number [`crate::lower`] gives one in the frame, so a value being passed and a value being worked
198/// on are the same shape of object in two places.
199#[must_use]
200pub fn on_the_stack(ty: Type) -> bool {
201    ty.is_float() && ty.is_scalar() && ty.bits() == 80
202}
203
204/// Whether what a function gives back is left on the x87 stack, which is one `long double` in
205/// `st(0)` or a `_Complex long double` with its real half there and its imaginary half in `st(1)`.
206///
207/// Those two are the whole of what SysV returns on that stack, and a structure holding a `long
208/// double` is not one of them, since the lowering sends that back through memory.
209#[must_use]
210pub fn back_on_x87(returns: &[Type]) -> bool {
211    matches!(returns.len(), 1 | 2) && returns.iter().all(|&ty| on_the_stack(ty))
212}
213
214/// How much room one takes in the argument area, as a size and an alignment.
215pub(crate) const X87_AREA: (u32, u32) = (16, 16);
216
217/// Why a value of that type cannot travel at all, or nothing if it can.
218///
219/// The width question and the file question in one place, so that the two ends of a call give the
220/// same answer about the same type, and so that a `return` this cannot make says the same thing
221/// about a type as the call that would have received it.
222///
223/// A `long double` is not one of them any more when it is an argument, since an argument of that
224/// type travels as bytes and [`on_the_stack`] is what says so before this is asked. What is left
225/// here is the value that comes back, because coming back is the one direction where it is not
226/// bytes: it arrives in `st(0)`, which is a register file this cannot name.
227#[must_use]
228pub fn refuses(ty: Type, insts: &Insts) -> Option<Missing> {
229    if (insts.arg)(ty).is_some() {
230        return None;
231    }
232    // A `long double` is the one type here that is in neither of the two files. Saying so is worth
233    // more than calling it a width, because eighty bits is a width this machine computes in and
234    // the file it computes in is what actually stands in the way.
235    if on_the_stack(ty) {
236        return Some(Missing::OnX87);
237    }
238    Some(Missing::Width)
239}
240
241/// What a function's parameters came to.
242#[derive(Debug, Default, Clone, PartialEq, Eq)]
243pub struct Arrived {
244    /// The register each parameter is in, in the order the parameters were given, so the caller can
245    /// bind each IR parameter to the one at its position.
246    pub regs: Vec<mir::Reg>,
247    /// The loads that read a parameter out of the caller's argument area, and how far up that area
248    /// each of them reads.
249    ///
250    /// Empty for almost every function, because almost every function has few enough parameters to
251    /// have been handed all of them in registers. The distance is from the bottom of the caller's
252    /// argument area, which is somewhere [`crate::finish`] works out and this cannot.
253    pub stack: Vec<(mir::Inst, u32)>,
254    /// How many general purpose argument registers the parameters took, and how many vector ones.
255    ///
256    /// Nothing about an ordinary function needs this. A variadic one does: the first argument its
257    /// signature does not name is the one after the last it does, so where each of the two walks
258    /// stopped is where `va_start` has to say the next argument begins.
259    pub took: (usize, usize),
260    /// How far up the caller's argument area the first argument the signature does not name is.
261    ///
262    /// However much of that area the named parameters took, on a convention that counts the two
263    /// register files apart, because there the area holds only the arguments no register was left
264    /// for. On one that counts them as one run it is not the same number: that area begins with the
265    /// shadow space the caller reserved, every argument owns one word of it whether it also arrived
266    /// in a register or not, and the named ones own the first few of those words. So it is where
267    /// the walk over the registers stopped, which is a position rather than a size, until the named
268    /// parameters have used every register and the two agree again.
269    pub beyond: u32,
270    /// The argument registers left over for the arguments the signature does not name, as the
271    /// register each was bound into and how far up the save area its slot is.
272    ///
273    /// Empty unless a save area was asked for. The ones a named parameter took are not here,
274    /// because their slots are behind where `va_start` sets the two offsets and nothing ever reads
275    /// them, so writing them would be fourteen stores where six are wanted.
276    pub spare: Vec<(mir::Reg, RegClass, u32)>,
277    /// The parameters that arrived in an argument register, as the position of each and how far up
278    /// the save area the slot of its register is.
279    ///
280    /// Empty unless a save area was asked for, and read only by a function that saves every
281    /// argument register rather than the spare ones, which is one holding `__builtin_apply_args`.
282    /// Together with [`Arrived::spare`] it is every register an argument can arrive in.
283    pub named: Vec<(usize, u32)>,
284}
285
286/// Binds a function's parameters to where the convention says they arrive.
287///
288/// # Errors
289///
290/// The first parameter this cannot bring in, and why. A function with one is reported rather
291/// than compiled, because the alternative is a function that reads an argument from wherever the
292/// last one happened to leave a register.
293pub fn entry(
294    out: &mut mir::Func,
295    block: mir::Block,
296    params: &[Param],
297    conv: &CallRegs,
298    insts: &Insts,
299    names: &mut Interner,
300    save: Option<Area>,
301) -> Result<Arrived, (usize, Missing)> {
302    // Where everything is, worked out before anything is written, both so that a parameter this
303    // cannot bring in stops the function before half of one is built and so that the two loops
304    // below can be two loops. Asking for the place of a parameter that cannot be brought in is
305    // still done, because every place after it depends on it and a reader stepping through this
306    // should see the same numbers a working version would.
307    let mut places = Places::new(conv);
308    let mut where_from = Vec::with_capacity(params.len());
309    for (index, &Param { ty, abi }) in params.iter().enumerate() {
310        // A structure the classification put in the argument area arrived as bytes, and the
311        // parameter the IR sees is a pointer to them. So there is nothing to bring in: the bytes
312        // are already in this function's frame, and what the pointer holds is where they are.
313        if let Abi::ByVal { size, align, drains } = abi {
314            let size = u32::try_from(size).map_err(|_| (index, Missing::TooBig))?;
315            where_from.push((ty, places.object(size, align), abi));
316            drain(&mut places, drains);
317            continue;
318        }
319        // And an eighty bit float, which arrives the same way for the same reason and is told
320        // apart only by the classification having said nothing about it: the front end passes it
321        // as a value of its own type, and it is this that knows the type is one that travels as
322        // bytes. What arrives is the address of those bytes, which is what an object in the
323        // argument area always hands over.
324        if on_the_stack(ty) {
325            let (size, align) = X87_AREA;
326            where_from.push((
327                ty,
328                places.on_stack(size, align),
329                Abi::ByVal { size: size.into(), align, drains: Drains::Nothing },
330            ));
331            continue;
332        }
333        let at = match (abi, conv.sret) {
334            // The address a result goes back through, on a convention with a register of its own
335            // for it, which takes no position from the arguments after it.
336            (Abi::Sret { .. }, Some(sret)) => Where::Reg(sret),
337            _ if ty.is_float() => places.float(float_bytes(ty)),
338            _ => places.integer(int_bytes(ty, conv)),
339        };
340        if let Some(missing) = refuses(ty, insts) {
341            return Err((index, missing));
342        }
343        where_from.push((ty, at, abi));
344    }
345    let took = (places.integers(), places.floats());
346    // Where the first argument the signature does not name is, which is the two sentences on
347    // [`Arrived::beyond`] written out. The run of words is contiguous from the bottom of the area on
348    // a convention that homes its register arguments, so a position multiplied by a word is the
349    // answer there until the positions run out and the named parameters start taking room of their
350    // own, at which point what they took is the answer again.
351    let reached = took.0 + took.1;
352    let beyond = match conv.shared_positions && reached < conv.int_args.len() {
353        true => conv.word * u32::try_from(reached).unwrap_or(0),
354        false => places.size(),
355    };
356    let mut arrived =
357        Arrived { regs: Vec::with_capacity(params.len()), took, beyond, ..Arrived::default() };
358
359    // Every pseudo first and everything else after, which is not a preference. A pseudo says a
360    // register holds an argument and defines nothing before it, so as far as the allocator can see
361    // the register was dead until then and is free to be used as a scratch. That is true of a
362    // register no pseudo has named yet, and it stops being true the moment one does. Anything that
363    // needs a scratch has to come after all of them, and a load out of the caller's stack needs one
364    // for the value it loads.
365    for (index, &(ty, at, _)) in where_from.iter().enumerate() {
366        let class = class_of(ty, conv);
367        let reg = out.new_vreg(class);
368        if class == conv.sse_class {
369            out.set_width(reg, float_bytes(ty));
370        }
371        arrived.regs.push(reg);
372        let Where::Reg(arrived_in) = at else { continue };
373        let head = (insts.arg)(ty).ok_or((index, Missing::Width))?;
374        let opcode = mir::Opcode::new(names.intern(head));
375        let operand = mir::Operand::write(reg, class).with(Constraint::Fixed(arrived_in));
376        out.build(block, opcode).operand(operand).finish();
377        if let Some(slot) = save.and_then(|area| slot_of(conv, area, arrived_in)) {
378            arrived.named.push((index, slot));
379        }
380    }
381    if let Some(area) = save {
382        arrived.spare = spare(out, block, conv, insts, names, area, arrived.took);
383    }
384
385    // The stack pointer is written down as the register to read through because it is the one that
386    // reaches the caller's stack in almost every function, and a realigned frame is the exception
387    // that [`crate::finish`] rewrites. Putting something here rather than nothing keeps the
388    // instruction printable and verifiable in between.
389    for (index, &(ty, at, abi)) in where_from.iter().enumerate() {
390        let Where::Stack(up) = at else { continue };
391        let class = class_of(ty, conv);
392        // The bytes of an object that travelled as bytes are read by whatever reads the parameter,
393        // and what the parameter is is their address, so this takes the address rather than a
394        // value out of it. Everything else about it is the load's, including the two fields
395        // [`crate::finish`] fills in, because where the caller's argument area is is the same
396        // question for both.
397        let name = match abi {
398            Abi::ByVal { .. } => insts.lea,
399            _ => (insts.load)(ty).ok_or((index, Missing::Width))?,
400        };
401        let opcode = mir::Opcode::new(names.intern(name));
402        let sp = mir::Operand::read(mir::Reg::physical(conv.stack_pointer), conv.int_class);
403        let made =
404            out.build(block, opcode).def(arrived.regs[index], class).mem(mir::Mem::at(sp)).finish();
405        arrived.stack.push((made, up));
406    }
407    Ok(arrived)
408}
409
410/// How far up a save area the slot of an argument register is, or nothing for a register the area
411/// has no slot for, which is the one a convention passes the address of a result in.
412fn slot_of(conv: &CallRegs, area: Area, reg: PhysReg) -> Option<u32> {
413    let files = [(false, conv.int_args), (true, conv.sse_args)];
414    files.into_iter().find_map(|(float, file)| {
415        let at = u32::try_from(file.iter().position(|&it| it == reg)?).ok()?;
416        (at < area.holds(float)).then(|| area.starts_at(float) + at * area.stride(float))
417    })
418}
419
420/// Binds the argument registers no parameter the signature names took, which are the ones the
421/// arguments it does not name arrived in.
422///
423/// One pseudo each and nothing else, for the reason the loop above them gives: what these do is say
424/// the register holds something, and the stores that put it in the save area are written by
425/// [`crate::lower`] once it has an address to store to, which is after every pseudo in the block.
426///
427/// Where each file's walk stopped is the file's own count on a convention that keeps two, and the
428/// sum of both on one that counts the files as a single run of positions, since there an argument
429/// of either kind steps the one counter. A file the area holds no slots of is skipped entirely,
430/// which is the vector file on the second kind: a variadic float travels in the general purpose
431/// register at its position as well, so the copy the walk reads is already the one being spilled.
432fn spare(
433    out: &mut mir::Func,
434    block: mir::Block,
435    conv: &CallRegs,
436    insts: &Insts,
437    names: &mut Interner,
438    area: Area,
439    took: (usize, usize),
440) -> Vec<(mir::Reg, RegClass, u32)> {
441    let word = Type::int(64);
442    let double = Type::float(rucc_ir::Float::F64);
443    let reached = |own: usize| if conv.shared_positions { took.0 + took.1 } else { own };
444    let files = [
445        (conv.int_args, reached(took.0), word, false),
446        (conv.sse_args, reached(took.1), double, true),
447    ];
448    let mut spare = Vec::new();
449    for (regs, taken, ty, float) in files {
450        let held = usize::try_from(area.holds(float)).unwrap_or(0);
451        let Some(head) = (insts.arg)(ty) else { continue };
452        let class = class_of(ty, conv);
453        for (index, &arrived_in) in regs.iter().enumerate().take(held).skip(taken) {
454            let reg = out.new_vreg(class);
455            let opcode = mir::Opcode::new(names.intern(head));
456            let operand = mir::Operand::write(reg, class).with(Constraint::Fixed(arrived_in));
457            out.build(block, opcode).operand(operand).finish();
458            let at = area.starts_at(float) + area.stride(float) * u32::try_from(index).unwrap_or(0);
459            spare.push((reg, class, at));
460        }
461    }
462    spare
463}
464
465/// The instructions this file writes, for one machine.
466///
467/// Every one of them is written here by hand rather than by a rule, for the reasons the module
468/// comment gives, so this file is where a machine's names for them have to come from. Each answer
469/// is the whole name, prefix and all, because each is interned as it is.
470///
471/// The four functions answer for the same set of types, and a type one of them has no name for is
472/// a type none of them should have: an argument that can arrive in a register but not be read off
473/// the stack is a function turned away for where its sixth argument happened to land.
474#[derive(Debug)]
475pub struct Insts {
476    /// The pseudo a parameter of that type arrives in a register as. See [`head_of`].
477    pub arg: fn(Type) -> Option<&'static str>,
478    /// The load that reads one of that type out of the argument area. See [`load_of`].
479    pub load: fn(Type) -> Option<&'static str>,
480    /// The store that writes one of that type into the argument area. See [`store_of`].
481    pub store: fn(Type) -> Option<&'static str>,
482    /// The pseudo a value of that type leaves in the register at that place. See [`ret_of`].
483    pub ret: fn(Type, usize) -> Option<&'static str>,
484    /// The call of a name. See [`CALL`].
485    pub call: &'static str,
486    /// The call of an address in a register. See [`CALL_REG`].
487    pub call_reg: &'static str,
488    /// The instruction that puts an address in a register without reading what is at it.
489    pub lea: &'static str,
490    /// The instruction that writes a small constant into a general purpose register, which is
491    /// what a byte count and a SysV vector count are.
492    pub small: &'static str,
493    /// The instruction that extends a value of that type the way the attribute asks, for the one
494    /// side of a call that owes the other the bits above it, and [`None`] where nothing does.
495    pub extend: fn(Type, Abi) -> Option<&'static str>,
496}
497
498/// The x86-64 ones.
499pub static X86_64: Insts = Insts {
500    arg: head_of,
501    load: load_of,
502    store: store_of,
503    ret: ret_of,
504    call: CALL,
505    call_reg: CALL_REG,
506    lea: "x64.lea_64",
507    small: "x64.mov_ri_32",
508    extend: |_, _| None,
509};
510
511/// What the instruction that calls a name is called.
512///
513/// Here rather than in a rule for the same reason the arguments are: a rule pattern sees one term
514/// and a call's operands are whatever the signature made them, so no pattern could name them.
515pub const CALL: &str = "x64.call";
516
517/// What the instruction that calls an address in a register is called.
518///
519/// A different instruction rather than the same one with a different operand, which is what the
520/// machine says too: one carries the distance to somewhere in the program and takes a relocation,
521/// and the other carries the register the address is in and takes none. Sharing an opcode would
522/// mean an instruction whose bytes depend on whether a field beside it happens to be set.
523pub const CALL_REG: &str = "x64.call_reg";
524
525/// One value a call passes.
526#[derive(Debug, Clone, Copy, PartialEq, Eq)]
527pub struct Passing {
528    /// The type it travels as, which for an object travelling as bytes is the pointer's rather
529    /// than the object's, because the pointer is what the machine IR has.
530    pub ty: Type,
531    /// The register holding it, or holding its address when the bytes are what travel.
532    pub reg: mir::Reg,
533    /// What the classification asked of it. The one thing read here is whether the object behind
534    /// the pointer is the argument, since everything else it can say is about a value that is
535    /// already in a register in the form it travels in.
536    pub abi: Abi,
537}
538
539/// What one call came to.
540#[derive(Debug, Clone, PartialEq, Eq)]
541pub struct Made {
542    /// The registers the value came back in, in the order the signature returns them, which is
543    /// empty for a call that gives nothing back and holds two for a structure that comes back in a
544    /// pair. Which register each of them is is the classification's answer and is worked out here
545    /// rather than in a table, for the reason the second half of [`Calling::returns`] gives.
546    pub results: Vec<mir::Reg>,
547    /// How many bytes below the stack pointer this call needs for the arguments it passes there.
548    ///
549    /// Not always zero for a call that passes everything in registers: a Windows caller reserves
550    /// thirty two bytes for the callee to spill its register arguments into whether it uses them
551    /// or not, and that reservation is this.
552    pub outgoing: u32,
553}
554
555/// Which of a call's values could not be passed, and why.
556#[derive(Debug, Clone, Copy, PartialEq, Eq)]
557pub struct Refused {
558    /// Its position among the arguments, or `None` for the value that comes back.
559    pub argument: Option<usize>,
560    /// What is wrong with where it travels.
561    pub missing: Missing,
562}
563
564/// What a call goes to.
565///
566/// The whole of the difference between the two calls. Everything else about them, which is what
567/// they pass and what comes back and which registers they destroy, is the signature's answer and
568/// is the same answer either way.
569#[derive(Debug, Clone, Copy, PartialEq, Eq)]
570pub enum Callee {
571    /// A name, which the linker resolves.
572    Named(Symbol),
573    /// An address in a register, which nothing resolves because there is nothing to resolve: the
574    /// value is not known until the program runs.
575    ///
576    /// The register is unconstrained, and it has to be, because every register the convention
577    /// does not preserve is one this instruction writes and every register an argument travels in
578    /// is spoken for. What is left is the registers the callee has to put back, which is where
579    /// the allocator will put the address, and it is the right answer for the same reason it is
580    /// the only one.
581    Through(mir::Reg),
582}
583
584/// One call, as everything about it that is not the function it is being built into.
585#[derive(Debug, Clone, Copy)]
586pub struct Calling<'a> {
587    /// What it calls.
588    pub callee: Callee,
589    /// What it passes, in the order the signature holds them, which is the order the convention
590    /// places them in.
591    pub args: &'a [Passing],
592    /// What comes back, which is empty for a call that gives nothing back, one type for a value,
593    /// and two for a structure small enough to come back in a pair of registers.
594    ///
595    /// A pair is placed here rather than named by a rule for the reason the arguments are: which
596    /// register each half goes in depends on the halves before it, since the two files are walked
597    /// separately, and a pattern over a term cannot see them.
598    pub returns: &'a [Type],
599    /// Whether the callee takes arguments beyond the ones its signature names, which is what says
600    /// whether it reads the count of vector registers the call passed arguments in.
601    pub variadic: bool,
602    /// How many of the arguments the signature does name, so that the ones past it can be told
603    /// apart from the ones before it.
604    ///
605    /// A convention that passes a variadic float in both register files needs that, because which
606    /// arguments get the second copy is exactly the ones the callee has no prototype for. Every
607    /// other convention treats the two the same and never asks.
608    pub named: usize,
609    /// Where the call was written, which every instruction built for it is filed under.
610    ///
611    /// A call is one of the few places in the machine IR where a run of instructions comes from no
612    /// term in the IR at all: the stores into the outgoing area, the copies a structure passed by
613    /// value is made of and the call itself are the convention's answer rather than anything a rule
614    /// matched. So there is nothing for them to inherit a span from, and without this the bytes of
615    /// an entire call statement are covered by whichever row came before them, which is usually the
616    /// line above. gcc names the line the call is written on over all of it.
617    ///
618    /// [`Span::DUMMY`] in a call this crate builds for itself, which is the copy into the argument
619    /// area that the runtime does, since that one is under whatever the call it belongs to is under.
620    pub at: Span,
621}
622
623/// Builds one call: what it passes, what comes back, and what it destroys.
624///
625/// # Errors
626///
627/// The first value this cannot pass, and why, before anything is written. A call with one is
628/// reported rather than compiled, because the alternative is a call that leaves an argument
629/// wherever the last one happened to put a register.
630///
631/// # Panics
632///
633/// If a call passes two gigabytes of arguments on the stack, which is a distance no offset in a
634/// frame can hold and a call no program makes.
635pub fn call(
636    out: &mut mir::Func,
637    block: mir::Block,
638    made: &Calling<'_>,
639    conv: &CallRegs,
640    insts: &Insts,
641    names: &mut Interner,
642) -> Result<Made, Refused> {
643    let &Calling { callee, args, returns, variadic, named, at: span } = made;
644    // Where everything goes, worked out before anything is built, so that a call this cannot make
645    // leaves no half of one behind.
646    let mut places = Places::new(conv);
647    let mut passed = Vec::with_capacity(args.len());
648    // The ones with no register left for them, as the store each of them becomes and how far up
649    // the outgoing area it writes. Almost always empty.
650    let mut on_stack = Vec::new();
651    // How many of them went in vector registers, which is what a SysV variadic callee is told.
652    let mut vectors = 0u32;
653    // The ones whose bytes travel rather than their address, as the register that address is in,
654    // how far up the outgoing area they go and which words the copy is made of. Almost always
655    // empty too, and never at the same time as a register: an object in the argument area is in
656    // the argument area whatever is left of the register files.
657    let mut as_bytes = Vec::new();
658    // The arguments beyond the ones the signature names that travel in a vector register and have
659    // to travel in a general purpose one at the same time, as the register each is in and the
660    // general purpose register its copy belongs in. Empty on every convention but the one that says
661    // so, and on that one this is what makes `printf("%f", x)` read the right register.
662    let mut in_both = Vec::new();
663    // Which argument position the next one that gets a register is at, which is only the same as
664    // the index when nothing ahead of it went to memory. A convention that counts the two register
665    // files as one run is what needs it: the general purpose register a variadic float's second
666    // copy goes in is the one at that position.
667    let mut position = 0usize;
668    // The ones the callee reads as a whole 32 bit register when they are narrower than that, as
669    // where in `passed` each is and the instruction that makes its upper bits what the callee
670    // expects. Only an argument in a register, since one in memory is read at its own width.
671    let mut widen = Vec::new();
672    for (index, &Passing { ty, reg, abi }) in args.iter().enumerate() {
673        let refused = |missing| Refused { argument: Some(index), missing };
674        // An eighty bit float is bytes in the argument area whatever the classification said, for
675        // the reason [`on_the_stack`] gives, and the register holding it holds their address. So it
676        // joins the objects below rather than being a case of its own, and the copy it becomes is
677        // the copy any other sixteen byte object gets.
678        let abi = match abi {
679            _ if on_the_stack(ty) => {
680                let (size, align) = X87_AREA;
681                Abi::ByVal { size: size.into(), align, drains: Drains::Nothing }
682            }
683            abi => abi,
684        };
685        if let Abi::ByVal { size, align, drains } = abi {
686            let size = u32::try_from(size).map_err(|_| refused(Missing::TooBig))?;
687            // One past the `...` is never packed, so it is words wherever it is.
688            let at = if variadic && index >= named {
689                places.on_stack(size, align)
690            } else {
691                places.object(size, align)
692            };
693            drain(&mut places, drains);
694            let Where::Stack(up) = at else {
695                unreachable!("an object in the argument area is in the argument area")
696            };
697            // Nothing here refuses a plan it did not get, because a copy the words give up on is
698            // a call to the runtime below. What the byte count has to fit in is the immediate the
699            // call passes it as, and an object that large is what is left of the old refusal.
700            let plan = crate::expand::plan(u64::from(size), align, conv.word);
701            let count = i32::try_from(size).map_err(|_| refused(Missing::TooBig))?;
702            as_bytes.push((reg, up, count, plan));
703            continue;
704        }
705        // The address a result comes back through goes in its own register where the convention
706        // has one, and is not an argument position, the same as on the other side in [`entry`].
707        if let (Abi::Sret { .. }, Some(sret)) = (abi, conv.sret) {
708            passed.push((reg, sret, class_of(ty, conv)));
709            continue;
710        }
711        // Apple's AArch64 puts every argument past the named ones in memory, a word or more each,
712        // whatever registers are left.
713        let unnamed = variadic && index >= named && conv.abi.variadic == Variadic::AlwaysMemory;
714        let at = if unnamed {
715            let bytes = int_bytes(ty, conv);
716            places.on_stack(bytes, bytes)
717        } else if ty.is_float() {
718            places.float(float_bytes(ty))
719        } else {
720            places.integer(int_bytes(ty, conv))
721        };
722        if let Some(missing) = refuses(ty, insts) {
723            return Err(refused(missing));
724        }
725        let class = class_of(ty, conv);
726        match at {
727            Where::Reg(at) => {
728                if let Some(name) = (insts.extend)(ty, abi) {
729                    widen.push((passed.len(), names.intern(name)));
730                }
731                // Only a register counts, because the count is of registers. An argument that went
732                // to memory is one the callee reads from memory whatever this says.
733                if class == conv.sse_class {
734                    vectors += 1;
735                    // Windows passes a float the callee has no prototype for in the vector register
736                    // and in the general purpose register at the same position, both at once,
737                    // because the callee has no way to know which file to look in and its walk over
738                    // the arguments reads the second one. An argument the signature does name needs
739                    // no second copy, since the callee's parameter says where it is.
740                    let both = conv.shared_positions && variadic && index >= named;
741                    if let Some(&also) = conv.int_args.get(position).filter(|_| both) {
742                        in_both.push((reg, also));
743                    }
744                }
745                passed.push((reg, at, class));
746                position += 1;
747            }
748            Where::Stack(up) => {
749                let store = (insts.store)(ty).ok_or(refused(Missing::Width))?;
750                on_stack.push((reg, class, names.intern(store), up));
751            }
752        }
753    }
754    // A `long double` comes back in `st(0)`, which is not a register in either file and not one
755    // this call can be said to write. So nothing is placed for it and nothing is constrained, and
756    // the call gives back no register at all: what takes the value off that stack is the `fstp`
757    // [`crate::lower`] writes straight after the call, which is the same shape every other use of
758    // the x87 stack is written in. A complex one is the same with its imaginary half in `st(1)`,
759    // and a second `fstp` takes that one off.
760    let comes_back =
761        if back_on_x87(returns) { Vec::new() } else { places_back(returns, conv, insts)? };
762
763    // A variadic callee on SysV reads how many vector registers the call passed arguments in and
764    // skips saving them when the answer is none, which is what makes `printf` with no floating
765    // point argument cheap. It is an obligation rather than an optimization: leaving whatever was
766    // in the register there makes the callee save a register file it was not given, and a count
767    // that is too low makes it read an argument out of a register nothing put one in.
768    let counted = if variadic { conv.vector_count } else { None };
769
770    for (at, name) in widen {
771        let (reg, _, class) = passed[at];
772        let wide = out.new_vreg(class);
773        out.build(block, mir::Opcode::new(name))
774            .at(span)
775            .def(wide, class)
776            .uses(reg, class)
777            .finish();
778        passed[at].0 = wide;
779    }
780
781    // The arguments that go to memory go there now, in front of the call and after everything this
782    // could have refused, so that a call it cannot make leaves no store behind either. The offset
783    // is written straight in rather than left for [`crate::finish`]: the outgoing area is at the
784    // bottom of the frame because that is where the callee looks for it, and the bottom of the
785    // frame is where the stack pointer already is.
786    for (reg, class, store, up) in on_stack {
787        let sp = mir::Operand::read(mir::Reg::physical(conv.stack_pointer), conv.int_class);
788        let up = i32::try_from(up).expect("an argument area under two gigabytes");
789        let build = out.build(block, mir::Opcode::new(store)).at(span);
790        build.uses(reg, class).mem(mir::Mem::at(sp).plus(up)).finish();
791    }
792
793    // How many bytes the copies below needed for calls of their own, which is nothing on a
794    // convention that passes three pointers in registers and thirty two bytes on the one that
795    // reserves a place for them anyway. The frame has to hear about it, since a call built here is
796    // still a call this function makes.
797    let mut nested = 0u32;
798
799    // And the objects whose bytes go there, as a load and a store for each word of each of them.
800    // This is the copy the caller owes a callee that takes a structure by value: the callee is
801    // free to write to what it was handed, so what it was handed cannot be the caller's own copy,
802    // and the argument area is where the convention says the caller's copy goes. The words are the
803    // same words `crate::expand` would have chosen for a `memcpy` of the same block, because they
804    // are chosen by the same function.
805    for (from, up, count, plan) in as_bytes {
806        let up = i32::try_from(up).expect("an argument area under two gigabytes");
807        let Some(plan) = plan else {
808            let what = Copying { from, up, count, span };
809            nested = nested.max(by_runtime(out, block, conv, insts, names, what));
810            continue;
811        };
812        for (at, width) in plan {
813            let ty = Type::int(width * 8);
814            let at = i32::try_from(at).expect("an object under two gigabytes");
815            let word = out.new_vreg(conv.int_class);
816            let load = names.intern(
817                (insts.load)(ty).ok_or(Refused { argument: None, missing: Missing::Width })?,
818            );
819            let there = mir::Operand::read(from, conv.int_class);
820            let build = out.build(block, mir::Opcode::new(load)).at(span);
821            build.def(word, conv.int_class).mem(mir::Mem::at(there).plus(at)).finish();
822            let store = names.intern(
823                (insts.store)(ty).ok_or(Refused { argument: None, missing: Missing::Width })?,
824            );
825            let sp = mir::Operand::read(mir::Reg::physical(conv.stack_pointer), conv.int_class);
826            let build = out.build(block, mir::Opcode::new(store)).at(span);
827            build.uses(word, conv.int_class).mem(mir::Mem::at(sp).plus(up + at)).finish();
828        }
829    }
830
831    // And the second copy of each float the callee has no prototype for, which is one `movq` out of
832    // the vector register it is already in. What the callee reads out of the general purpose
833    // register is the sixty four bits and not a value of any type, so the bits are what move, and
834    // the copy joins the arguments rather than being a thing of its own: it is passed in a register
835    // the convention names, which is what every other argument here is.
836    for (from, into) in in_both {
837        let word = out.new_vreg(conv.int_class);
838        let movq = mir::Opcode::new(names.intern("x64.movq_from_xmm"));
839        out.build(block, movq)
840            .at(span)
841            .def(word, conv.int_class)
842            .uses(from, conv.sse_class)
843            .finish();
844        passed.push((word, into, conv.int_class));
845    }
846
847    // The definitions first and the reads after, which is the order every operand vector in the
848    // machine IR is in and the order `rucc_mir::defs` counts.
849    let mut operands = Vec::with_capacity(args.len() + conv.int_order.len() + 2);
850    let results: Vec<mir::Reg> = comes_back
851        .iter()
852        .map(|&(at, class)| {
853            let reg = out.new_vreg(class);
854            operands.push(mir::Operand::write(reg, class).with(Constraint::Fixed(at)));
855            reg
856        })
857        .collect();
858    // One list per file, because a physical register is a number and the class is what says which
859    // file it is a number in. One list would have `xmm0` blocking `rax`.
860    let spoken_for = |class: RegClass| -> Vec<PhysReg> {
861        comes_back
862            .iter()
863            .filter(|&&(_, at)| at == class)
864            .map(|&(reg, _)| reg)
865            .chain(counted.filter(|_| class == conv.int_class))
866            .chain(passed.iter().filter(|&&(_, _, at)| at == class).map(|&(_, reg, _)| reg))
867            .collect()
868    };
869    let named = spoken_for(conv.int_class);
870    for &reg in conv.int_order {
871        if !conv.preserves_int(reg) && !named.contains(&reg) {
872            operands.push(mir::Operand::write(mir::Reg::physical(reg), conv.int_class));
873        }
874    }
875    let named = spoken_for(conv.sse_class);
876    for &reg in conv.sse_order {
877        if named.contains(&reg) {
878            continue;
879        }
880        // One the convention keeps only the bottom of is written above that, which leaves a value
881        // narrow enough to fit where it is and takes the register from anything wider.
882        let clobber = mir::Operand::write(mir::Reg::physical(reg), conv.sse_class);
883        match conv.sse_kept {
884            _ if !conv.preserves_sse(reg) => operands.push(clobber),
885            Some(kept) => operands.push(clobber.with(Constraint::Above(kept))),
886            None => {}
887        }
888    }
889    // The address in front of the arguments, because a call through one is written with the
890    // register it goes through and nothing in the operand vector is at a place a table could name.
891    // First read is a place that does not depend on the signature, which is what
892    // [`rucc_target::x86_64::Arg::Through`] is written against.
893    if let Callee::Through(reg) = callee {
894        operands.push(mir::Operand::read(reg, conv.int_class));
895    }
896    for (reg, at, class) in passed {
897        operands.push(mir::Operand::read(reg, class).with(Constraint::Fixed(at)));
898    }
899    if let Some(at) = counted {
900        let count = out.new_vreg(conv.int_class);
901        let zero = mir::Opcode::new(names.intern(insts.small));
902        out.build(block, zero).at(span).def(count, conv.int_class).imm(i64::from(vectors)).finish();
903        operands.push(mir::Operand::read(count, conv.int_class).with(Constraint::Fixed(at)));
904    }
905
906    let opcode = mir::Opcode::new(names.intern(match callee {
907        Callee::Named(_) => insts.call,
908        Callee::Through(_) => insts.call_reg,
909    }));
910    let mut build = out.build(block, opcode).at(span);
911    if let Callee::Named(symbol) = callee {
912        build = build.symbol(symbol);
913    }
914    for operand in operands {
915        build = build.operand(operand);
916    }
917    build.finish();
918    Ok(Made { results, outgoing: places.size().max(nested) })
919}
920
921/// One object whose bytes go into the argument area by a call to the runtime.
922#[derive(Debug, Clone, Copy)]
923struct Copying {
924    /// The register its address is in.
925    from: mir::Reg,
926    /// How far up the outgoing area its copy goes.
927    up: i32,
928    /// How many bytes it is.
929    count: i32,
930    /// Where the call it is an argument of was written.
931    span: Span,
932}
933
934/// One object with more words than a copy into the argument area unrolls to, copied there by a
935/// call to the runtime, and how many bytes that call itself needed below the stack pointer.
936///
937/// Which routine it is is [`crate::capability`]'s answer and not a name written here, because a
938/// call standing in for an operation the machine has no instruction for is what that table is a
939/// list of, and a copy too large to unroll is already on it: this is the same row `crate::expand`
940/// reads for a `memcpy` in the IR of the same size.
941fn by_runtime(
942    out: &mut mir::Func,
943    block: mir::Block,
944    conv: &CallRegs,
945    insts: &Insts,
946    names: &mut Interner,
947    what: Copying,
948) -> u32 {
949    let Copying { from, up, count, span } = what;
950    let routine = capability::libcall(rucc_ir::Opcode::Memcpy, "big")
951        .expect("the runtime copies a block too large to unroll");
952
953    // Where the copy goes, which is a distance up the outgoing area and so is the stack pointer
954    // plus that distance. A `lea` rather than an add, because the stack pointer is not this
955    // function's to move and what the call wants is the address in a register of the allocator's
956    // choosing.
957    let sp = mir::Operand::read(mir::Reg::physical(conv.stack_pointer), conv.int_class);
958    let into = out.new_vreg(conv.int_class);
959    let lea = mir::Opcode::new(names.intern(insts.lea));
960    out.build(block, lea)
961        .at(span)
962        .def(into, conv.int_class)
963        .mem(mir::Mem::at(sp).plus(up))
964        .finish();
965
966    // And how many bytes, which C takes as a `size_t` and this has as a number. A thirty two bit
967    // move carries it, because writing the low half of a general purpose register clears the high
968    // half, so the sixty four bit count it becomes is the count as long as the count fits in the
969    // immediate, which is what the caller checked before anything was built.
970    let bytes = out.new_vreg(conv.int_class);
971    let mov = mir::Opcode::new(names.intern(insts.small));
972    out.build(block, mov).at(span).def(bytes, conv.int_class).imm(i64::from(count)).finish();
973
974    let args = [
975        Passing { ty: Type::PTR, reg: into, abi: Abi::Plain },
976        Passing { ty: Type::PTR, reg: from, abi: Abi::Plain },
977        Passing { ty: Type::int(conv.word * 8), reg: bytes, abi: Abi::Plain },
978    ];
979    let made = Calling {
980        callee: Callee::Named(names.intern(routine)),
981        args: &args,
982        returns: &[],
983        variadic: false,
984        named: args.len(),
985        at: span,
986    };
987    // Three pointer sized arguments and nothing coming back is a call every convention here has
988    // registers for, so the only way this could refuse is a convention with fewer than three
989    // argument registers, and there is no such convention.
990    call(out, block, &made, conv, insts, names)
991        .expect("the runtime's copy passes three words and takes nothing back")
992        .outgoing
993}
994
995/// Which register each value comes back in, walked the way the arguments are.
996///
997/// The two files are counted separately, because a structure of a `double` and a `long` comes back
998/// with the `double` in the first vector register and the `long` in the first integer one, and a
999/// single count would put the second half one place further along a list it is not on.
1000///
1001/// # Errors
1002///
1003/// The first value that cannot come back at all, and why, so that a call this cannot make leaves
1004/// nothing behind. Nothing here reports which value it was, because the caller has one answer for
1005/// all of them: the value that comes back is not an argument and has no position among them.
1006fn places_back(
1007    returns: &[Type],
1008    conv: &CallRegs,
1009    insts: &Insts,
1010) -> Result<Vec<(PhysReg, RegClass)>, Refused> {
1011    let refused = |missing| Refused { argument: None, missing };
1012    let mut back = Vec::with_capacity(returns.len());
1013    let (mut ints, mut sses) = (0usize, 0usize);
1014    for &ty in returns {
1015        if let Some(missing) = refuses(ty, insts) {
1016            return Err(refused(missing));
1017        }
1018        let class = class_of(ty, conv);
1019        let (file, at) = if class == conv.sse_class {
1020            (conv.sse_returns, &mut sses)
1021        } else {
1022            (conv.int_returns, &mut ints)
1023        };
1024        let reg = *file.get(*at).ok_or_else(|| refused(Missing::NoRoom))?;
1025        *at += 1;
1026        back.push((reg, class));
1027    }
1028    Ok(back)
1029}
1030
1031/// Which of the four widths a value travels at, where a truth value travels as the byte it lives
1032/// in.
1033///
1034/// [`crate::term::slot`] is the question the rule set asks and it answers nothing for one bit,
1035/// because there is no register of that width and so no instruction written at it. A convention
1036/// asks a different question. It has nothing narrower than a byte to put an argument in either,
1037/// and what it says about the one type that is a bit is that the byte holding it is the argument,
1038/// with the seven bits above unspecified. So the two lists differ by exactly this entry.
1039///
1040/// It is written here and not in [`crate::term::slot`] because moving it there would tell the rule
1041/// set that one bit is a byte, and then every byte rule in the file would match a term that is not
1042/// one. What the convention needs is narrower: a name for the register an argument arrives in, and
1043/// that name says a width because a listing is easier to read when it does.
1044fn place(ty: Type) -> Option<usize> {
1045    if crate::term::is_bit(ty) { Some(0) } else { crate::term::slot(ty) }
1046}
1047
1048/// What the pseudo for an argument of that type is called.
1049///
1050/// The width is in the name for the same reason it is in every other opcode here: it is what the
1051/// instruction is about. Nothing encodes it, so nothing depends on it being right, but a listing
1052/// that says an argument arrived and does not say how much of it did is a listing worth less.
1053///
1054/// Which widths there are is `place` above, and not a list of its own, because it has to be the
1055/// same list the three below use. An argument brought in at a width nothing downstream has a name
1056/// for is a register nothing could then read, and a width the others cover that this refuses is a
1057/// function turned away for no reason. Asking one question in one place is what keeps the four
1058/// answers from drifting, and an address is what they used to disagree about.
1059#[must_use]
1060pub fn head_of(ty: Type) -> Option<&'static str> {
1061    // The format that fills a whole vector register, which travels in one of them: the psABI
1062    // classifies it SSE and SSEUP, and those two eightbytes are the one register the pair names
1063    // rather than two registers.
1064    if crate::term::is_quad(ty) {
1065        return Some("x64.arg_val_f128");
1066    }
1067    // The half, which arrives in the low sixteen bits of a vector register the same way a `float`
1068    // arrives in the low thirty two. It is a name of its own rather than the `f32` one for the
1069    // reason every other width here has a name of its own: what arrived is two bytes, and a
1070    // listing that said four would be saying something the convention does not.
1071    if crate::term::is_half(ty) {
1072        return Some("x64.arg_val_f16");
1073    }
1074    if let Some(at) = crate::term::float_slot(ty) {
1075        return Some(["x64.arg_val_f32", "x64.arg_val_f64"][at]);
1076    }
1077    let names = ["x64.arg_val_8", "x64.arg_val_16", "x64.arg_val_32", "x64.arg_val_64"];
1078    Some(names[place(ty)?])
1079}
1080
1081/// What the instruction that reads an argument of that type out of memory is called.
1082///
1083/// Keyed off the same two questions [`head_of`] asks and answering for the same set of types, so
1084/// that a parameter this compiler can bring in from a register is one it can bring in from the
1085/// caller's stack as well. A width one of them covered and the other did not would be a function
1086/// turned away for where its sixth argument happened to land.
1087///
1088/// Reading a narrow argument at its own width and not at a word is deliberate. The caller wrote a
1089/// whole word, but what it put in the part above the value is not something the convention says, so
1090/// the bits this reads are exactly the bits that mean anything. That is the same thing an argument
1091/// arriving in a register gets: `x64.arg_val_8` says the low byte of that register is the argument
1092/// and says nothing at all about the rest of it.
1093#[must_use]
1094pub fn load_of(ty: Type) -> Option<&'static str> {
1095    // Sixteen bytes, which is the whole register and is also the whole value, so the instruction
1096    // a spill uses and the instruction an argument uses are the same one here. They are two
1097    // different instructions at the two narrower formats because there the value is part of the
1098    // register, and at this format there is no part of it to leave behind.
1099    if crate::term::is_quad(ty) {
1100        return Some("x64.movaps_rm");
1101    }
1102    // Two bytes out of the argument area and into the low lane of a vector register, which is one
1103    // instruction and is the same one a rule writes for a program's own read of a `_Float16`.
1104    if crate::term::is_half(ty) {
1105        return Some("x64.pinsrw_rm");
1106    }
1107    if let Some(at) = crate::term::float_slot(ty) {
1108        return Some(["x64.movss_rm", "x64.movsd_rm"][at]);
1109    }
1110    let names = ["x64.mov_rm_8", "x64.mov_rm_16", "x64.mov_rm_32", "x64.mov_rm_64"];
1111    Some(names[place(ty)?])
1112}
1113
1114/// What the instruction that writes an argument of that type into memory is called.
1115///
1116/// The mirror of [`load_of`], keyed off the same two questions and answering for the same set of
1117/// types, so that the two ends of one call agree about what travels. A type a callee can read out
1118/// of the argument area and a caller cannot write into it would be a call turned away for a reason
1119/// the function it calls does not have.
1120///
1121/// Writing a narrow argument at its own width leaves whatever was already in the rest of the word.
1122/// That is allowed, and it is what [`load_of`] is written against: the convention does not say what
1123/// is above the value, so the callee reads only the bits that mean anything and neither end has to
1124/// agree about the rest.
1125#[must_use]
1126pub fn store_of(ty: Type) -> Option<&'static str> {
1127    if crate::term::is_quad(ty) {
1128        return Some("x64.movaps_mr");
1129    }
1130    // The half goes out four bytes wide, which is the one place here where the instruction is not
1131    // the width of the value. SSE2 has no store of sixteen bits out of a vector register: the form
1132    // of `pextrw` that writes memory arrived with SSE4.1 and is above this target's baseline, so
1133    // the choices are a four byte store or a pair of instructions through a general purpose
1134    // register, and a pair is not something one name can be.
1135    //
1136    // Four bytes is safe here and would not be everywhere. An argument on the stack sits in an
1137    // eightbyte of its own, the paragraph above says the convention promises nothing about the
1138    // part of it above the value, and [`load_of`] reads back exactly the two bytes that mean
1139    // anything. What lands in the two bytes beside them is whatever was in the register, which is
1140    // no more than any other narrow argument leaves behind.
1141    if crate::term::is_half(ty) {
1142        return Some("x64.movss_mr");
1143    }
1144    if let Some(at) = crate::term::float_slot(ty) {
1145        return Some(["x64.movss_mr", "x64.movsd_mr"][at]);
1146    }
1147    let names = ["x64.mov_mr_8", "x64.mov_mr_16", "x64.mov_mr_32", "x64.mov_mr_64"];
1148    Some(names[place(ty)?])
1149}
1150
1151/// What the instruction that leaves a returned value in its register is called, for the value at
1152/// that place in its own register file.
1153///
1154/// Keyed off the same two questions [`head_of`] asks, so a type this can give back is a type it can
1155/// take in. The place is the one a call counted to when it laid the return out, which is per file
1156/// rather than over the whole list: a structure of a `double` and a `long` gives both of them back
1157/// at place zero.
1158///
1159/// The register itself is not here. It is in the operand table in `rucc_target::x86_64`, which is
1160/// where the first one has always been, and the two names below are how a value says which of the
1161/// two it is. A convention with more than two registers to come back in would need more names, and
1162/// there is none, which is what the `None` at the end is about.
1163#[must_use]
1164pub fn ret_of(ty: Type, at: usize) -> Option<&'static str> {
1165    if crate::term::is_quad(ty) {
1166        return Some(*["x64.ret_val_f128", "x64.ret_val2_f128"].get(at)?);
1167    }
1168    if crate::term::is_half(ty) {
1169        return Some(*["x64.ret_val_f16", "x64.ret_val2_f16"].get(at)?);
1170    }
1171    if let Some(width) = crate::term::float_slot(ty) {
1172        let names =
1173            [["x64.ret_val_f32", "x64.ret_val_f64"], ["x64.ret_val2_f32", "x64.ret_val2_f64"]];
1174        return Some(names.get(at)?[width]);
1175    }
1176    let names = [
1177        ["x64.ret_val_8", "x64.ret_val_16", "x64.ret_val_32", "x64.ret_val_64"],
1178        ["x64.ret_val2_8", "x64.ret_val2_16", "x64.ret_val2_32", "x64.ret_val2_64"],
1179    ];
1180    Some(names.get(at)?[place(ty)?])
1181}
1182
1183#[cfg(test)]
1184mod tests {
1185    use rucc_target::Convention;
1186    use rucc_target::x86_64::{REGS, SYSV, WIN64};
1187
1188    use super::*;
1189
1190    /// Those types as parameters that travel as the values they are, which is every one of them
1191    /// that is not a structure the classification put in the argument area.
1192    fn plain(params: &[Type]) -> Vec<Param> {
1193        params.iter().copied().map(Param::new).collect()
1194    }
1195
1196    /// The parameters of a function under a convention, as machine IR text.
1197    fn bind(params: &[Type], conv: &CallRegs) -> String {
1198        let mut names = Interner::new();
1199        let mut out = mir::Func::new(names.intern("f"));
1200        let block = out.create_block();
1201        entry(&mut out, block, &plain(params), conv, &X86_64, &mut names, None)
1202            .expect("every parameter arrives");
1203        mir::print_func(&out, &names, &REGS)
1204    }
1205
1206    #[test]
1207    fn the_first_arguments_arrive_where_the_convention_puts_them() {
1208        let i32 = Type::int(32);
1209        assert_eq!(
1210            bind(&[i32, i32, Type::int(64)], &SYSV),
1211            "mfunc @f {\nblock0:\n    %0:gpr($rdi) = x64.arg_val_32\n    \
1212             %1:gpr($rsi) = x64.arg_val_32\n    %2:gpr($rdx) = x64.arg_val_64\n}\n"
1213        );
1214    }
1215
1216    #[test]
1217    fn the_other_convention_puts_the_same_arguments_somewhere_else() {
1218        // The first argument is in `rcx` here and in `rdi` above, which is the difference that
1219        // makes a SysV binary calling a Windows one read the wrong value rather than fail.
1220        let i64 = Type::int(64);
1221        assert_eq!(
1222            bind(&[i64, i64], &WIN64),
1223            "mfunc @f {\nblock0:\n    %0:gpr($rcx) = x64.arg_val_64\n    \
1224             %1:gpr($rdx) = x64.arg_val_64\n}\n"
1225        );
1226    }
1227
1228    /// The parameters of a function under a convention, and what each of the ones that arrived in
1229    /// memory is waiting on.
1230    fn arrive(params: &[Type], conv: &CallRegs) -> (String, Vec<u32>) {
1231        let mut names = Interner::new();
1232        let mut out = mir::Func::new(names.intern("f"));
1233        let block = out.create_block();
1234        let arrived = entry(&mut out, block, &plain(params), conv, &X86_64, &mut names, None)
1235            .expect("every parameter");
1236        let up = arrived.stack.iter().map(|&(_, up)| up).collect();
1237        (mir::print_func(&out, &names, &REGS), up)
1238    }
1239
1240    #[test]
1241    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
1242        let (text, up) = arrive(&[Type::int(64); 7], &SYSV);
1243
1244        // Six of them got registers and the seventh did not, so the seventh is a load rather than
1245        // a pseudo. It reads through the stack pointer with nothing in its displacement, because
1246        // where the caller's argument area is from in here is a distance into a frame that does
1247        // not exist yet, and it is at the bottom of that area because it is the first one in it.
1248        assert_eq!(up, [0]);
1249        assert!(text.contains("%6:gpr = x64.mov_rm_64 [$rsp]"), "{text}");
1250        assert_eq!(text.matches("x64.arg_val_64").count(), 6, "{text}");
1251    }
1252
1253    #[test]
1254    fn the_other_convention_runs_out_of_registers_three_arguments_earlier() {
1255        let (text, up) = arrive(&[Type::int(64); 7], &WIN64);
1256
1257        // Windows passes four integers in registers and reserves thirty two bytes below the call
1258        // whether they are used or not, so the fifth argument is not at the bottom of the argument
1259        // area but above the shadow space, and the three after it follow it a word at a time.
1260        assert_eq!(up, [32, 40, 48]);
1261        assert_eq!(text.matches("x64.arg_val_64").count(), 4, "{text}");
1262        assert!(text.contains("%4:gpr = x64.mov_rm_64 [$rsp]"), "{text}");
1263    }
1264
1265    /// The parameters of a function under a convention, with one of them a structure whose bytes
1266    /// travel, and what each of the ones that arrived in memory is waiting on.
1267    fn arrive_with(params: &[Param], conv: &CallRegs) -> (String, Vec<u32>) {
1268        let mut names = Interner::new();
1269        let mut out = mir::Func::new(names.intern("f"));
1270        let block = out.create_block();
1271        let arrived = entry(&mut out, block, params, conv, &X86_64, &mut names, None)
1272            .expect("every parameter");
1273        let up = arrived.stack.iter().map(|&(_, up)| up).collect();
1274        (mir::print_func(&out, &names, &REGS), up)
1275    }
1276
1277    #[test]
1278    fn a_structure_that_arrived_as_bytes_is_an_address_and_not_a_load() {
1279        let byval =
1280            Param::with_abi(Type::PTR, Abi::ByVal { size: 32, align: 8, drains: Drains::Nothing });
1281        let (text, up) =
1282            arrive_with(&[Param::new(Type::int(32)), byval, Param::new(Type::int(32))], &SYSV);
1283
1284        // `int f(int a, struct Big b, int c)`. The bytes of `b` are already in this function, at
1285        // the bottom of the caller's argument area, so nothing is read out of them here: what the
1286        // parameter is is where they are, which is one address. The two integers still travel in
1287        // registers, because an object in the argument area takes no register and the arguments
1288        // behind it do not shift along.
1289        assert_eq!(up, [0]);
1290        assert_eq!(text.matches("x64.arg_val_32").count(), 2, "{text}");
1291        assert!(text.contains("%2:gpr = x64.lea_64 [$rsp]"), "{text}");
1292        assert!(!text.contains("mov_rm"), "nothing is read out of the bytes: {text}");
1293    }
1294
1295    #[test]
1296    fn the_argument_behind_a_structure_that_travelled_as_bytes_is_above_all_of_them() {
1297        let byval =
1298            Param::with_abi(Type::PTR, Abi::ByVal { size: 24, align: 16, drains: Drains::Nothing });
1299        let params: Vec<Param> = (0..7).map(|_| Param::new(Type::int(64))).collect();
1300        let (_, up) = arrive_with(&[&params[..], &[byval], &params[..1]].concat(), &SYSV);
1301
1302        // Six integers take the six registers, the seventh is at the bottom of the argument area,
1303        // and the structure is above it at the alignment its type asks for rather than at a word.
1304        // The one behind the structure is above all twenty four of its bytes, rounded up to a
1305        // whole number of words, because the area is a run of words.
1306        assert_eq!(up, [0, 16, 40]);
1307    }
1308
1309    /// A parameter narrower than a word is read at its own width rather than at a word, and one in
1310    /// the other register file is read with the other file's instruction. Both are the same list
1311    /// [`head_of`] answers from, which is what stops a function being turned away for the width of
1312    /// its seventh argument alone.
1313    #[test]
1314    fn what_a_stack_argument_is_read_with_is_its_own_width_and_its_own_file() {
1315        let f32 = Type::float(rucc_ir::Float::F32);
1316        let params = [Type::int(64), Type::int(64), Type::int(64), Type::int(64), Type::int(8)];
1317        let (text, up) = arrive(&params, &WIN64);
1318        assert_eq!(up, [32]);
1319        assert!(text.contains("x64.mov_rm_8 [$rsp]"), "{text}");
1320
1321        let floats = [f32; 5];
1322        let (text, up) = arrive(&floats, &WIN64);
1323        assert_eq!(up, [32]);
1324        assert!(text.contains("%4:xmm = x64.movss_rm [$rsp]"), "{text}");
1325    }
1326
1327    /// Every type a parameter can arrive in a register at is one it can be read from memory at.
1328    /// The two lists are keyed off the same two questions so that they cannot drift, and this is
1329    /// what says so: a width one covered and the other did not would be a function turned away for
1330    /// where its arguments happened to land rather than for anything about it.
1331    #[test]
1332    fn the_two_lists_of_widths_answer_for_the_same_types() {
1333        let types = [
1334            Type::int(1),
1335            Type::int(8),
1336            Type::int(16),
1337            Type::int(32),
1338            Type::int(64),
1339            Type::int(128),
1340            Type::PTR,
1341            Type::float(rucc_ir::Float::F32),
1342            Type::float(rucc_ir::Float::F64),
1343            Type::float(rucc_ir::Float::F80),
1344            Type::float(rucc_ir::Float::F128),
1345        ];
1346        for ty in types {
1347            assert_eq!(head_of(ty).is_some(), load_of(ty).is_some(), "{ty:?}");
1348            assert_eq!(head_of(ty).is_some(), store_of(ty).is_some(), "{ty:?}");
1349            assert_eq!(head_of(ty).is_some(), ret_of(ty, 0).is_some(), "{ty:?}");
1350        }
1351    }
1352
1353    /// A hundred and twenty eight bit float arrives in a vector register like the two narrower
1354    /// formats, and it takes one of them rather than two: the psABI classifies it SSE and SSEUP,
1355    /// and what that pair names is the one register both eightbytes are in.
1356    ///
1357    /// The second float here is what says so. If the quad had taken two vector registers the
1358    /// `double` after it would be in `xmm2`.
1359    #[test]
1360    fn a_quad_float_arrives_in_one_vector_register_and_not_in_two() {
1361        let quad = Type::float(rucc_ir::Float::F128);
1362        let f64 = Type::float(rucc_ir::Float::F64);
1363        assert_eq!(
1364            bind(&[quad, f64], &SYSV),
1365            "mfunc @f {\nblock0:\n    %0:xmm($xmm0) = x64.arg_val_f128\n    \
1366             %1:xmm($xmm1) = x64.arg_val_f64\n}\n"
1367        );
1368    }
1369
1370    /// And it is not the width that was refused before there was an instruction to move it with,
1371    /// which is the one thing about this type that used to turn a whole function away.
1372    #[test]
1373    fn a_quad_float_is_no_longer_a_width_nothing_can_carry() {
1374        assert_eq!(refuses(Type::float(rucc_ir::Float::F128), &X86_64), None);
1375        assert_eq!(refuses(Type::float(rucc_ir::Float::F80), &X86_64), Some(Missing::OnX87));
1376        assert_eq!(refuses(Type::int(128), &X86_64), Some(Missing::Width));
1377    }
1378
1379    /// A float arrives in the other file, and the two files are counted apart on SysV: the
1380    /// integer here is the first integer argument and the float is the first float one, so they
1381    /// are in `rdi` and `xmm0` rather than in the first and second of anything.
1382    #[test]
1383    fn a_float_arrives_in_a_vector_register_and_is_counted_apart_from_the_integers() {
1384        let f32 = Type::float(rucc_ir::Float::F32);
1385        let f64 = Type::float(rucc_ir::Float::F64);
1386        assert_eq!(
1387            bind(&[Type::int(32), f64, f32], &SYSV),
1388            "mfunc @f {\nblock0:\n    %0:gpr($rdi) = x64.arg_val_32\n    \
1389             %1:xmm($xmm0) = x64.arg_val_f64\n    %2:xmm($xmm1) = x64.arg_val_f32\n}\n"
1390        );
1391    }
1392
1393    /// Windows counts the two files together, so the same three arguments land in different
1394    /// registers: the float is the second argument and takes the second vector register rather
1395    /// than the first, which is the difference that makes a mismatched call read the wrong value.
1396    #[test]
1397    fn the_other_convention_counts_the_two_files_as_one_run_of_positions() {
1398        let f64 = Type::float(rucc_ir::Float::F64);
1399        assert_eq!(
1400            bind(&[Type::int(32), f64, Type::int(64)], &WIN64),
1401            "mfunc @f {\nblock0:\n    %0:gpr($rcx) = x64.arg_val_32\n    \
1402             %1:xmm($xmm1) = x64.arg_val_f64\n    %2:gpr($r8) = x64.arg_val_64\n}\n"
1403        );
1404    }
1405
1406    /// A `long double` is in neither file and travels in the argument area, which is what SysV's
1407    /// X87 classification comes to. So it arrives the way a structure the classification put in
1408    /// memory arrives, as the address of its bytes in a general purpose register, and it does that
1409    /// while the vector file is untouched: this one is in the argument area because of what it is
1410    /// rather than because the registers ran out.
1411    #[test]
1412    fn a_long_double_arrives_as_the_address_of_its_bytes_in_the_argument_area() {
1413        let params = [Type::int(32), Type::float(rucc_ir::Float::F80)];
1414        assert_eq!(
1415            bind(&params, &SYSV),
1416            "mfunc @f {\nblock0:\n    %0:gpr($rdi) = x64.arg_val_32\n    \
1417             %1:gpr = x64.lea_64 [$rsp]\n}\n"
1418        );
1419    }
1420
1421    /// It still cannot come back beside another value, and what it is turned away for says which
1422    /// file is in the way rather than calling eighty bits a width no register holds. A pair comes
1423    /// back in a pair of registers and there is no pair with the x87 stack in it.
1424    #[test]
1425    fn a_long_double_in_a_pair_is_reported_as_the_x87_stack_it_travels_on() {
1426        let returns = [Type::float(rucc_ir::Float::F80), Type::int(64)];
1427        assert_eq!(
1428            make(&[], &returns, false, &SYSV).2,
1429            Err(Refused { argument: None, missing: Missing::OnX87 })
1430        );
1431    }
1432
1433    /// One call to `g`, with a register for each argument arriving in the block that makes it.
1434    ///
1435    /// A variadic call here names none of its arguments, which is the shape that asks the most of a
1436    /// convention. [`made_naming`] is for the tests that care where the line between the named ones
1437    /// and the rest actually falls.
1438    fn make(
1439        args: &[Type],
1440        returns: &[Type],
1441        variadic: bool,
1442        conv: &CallRegs,
1443    ) -> (Interner, mir::Func, Result<Made, Refused>) {
1444        let named = if variadic { 0 } else { args.len() };
1445        made_naming(args, returns, named, variadic, conv)
1446    }
1447
1448    /// The same, for a callee whose signature names that many of the arguments.
1449    fn made_naming(
1450        args: &[Type],
1451        returns: &[Type],
1452        named: usize,
1453        variadic: bool,
1454        conv: &CallRegs,
1455    ) -> (Interner, mir::Func, Result<Made, Refused>) {
1456        let mut names = Interner::new();
1457        let mut out = mir::Func::new(names.intern("f"));
1458        let block = out.create_block();
1459        let passed: Vec<Passing> = args
1460            .iter()
1461            .map(|&ty| Passing {
1462                ty,
1463                reg: out.append_param(block, class_of(ty, conv)),
1464                abi: Abi::Plain,
1465            })
1466            .collect();
1467        let callee = Callee::Named(names.intern("g"));
1468        let what = Calling { callee, args: &passed, returns, variadic, named, at: Span::DUMMY };
1469        let made = call(&mut out, block, &what, conv, &X86_64, &mut names);
1470        (names, out, made)
1471    }
1472
1473    /// What the call in that function reads and writes, by register name, in the order the
1474    /// operands are in.
1475    fn operands(func: &mir::Func) -> (Vec<String>, Vec<String>) {
1476        let block = func.entry().expect("a function with a block in it");
1477        let call = func.terminator(block).expect("the call is the last thing in the block");
1478        let name = |operand: &mir::Operand| match (operand.reg.phys(), operand.constraint) {
1479            (Some(reg), _) | (None, Constraint::Fixed(reg)) => {
1480                REGS.name(operand.class, reg).expect("a register the file describes").to_string()
1481            }
1482            _ => format!("{:?}", operand.reg),
1483        };
1484        let mut written = Vec::new();
1485        let mut read = Vec::new();
1486        for operand in &func[func[call].operands] {
1487            let into = if operand.role == mir::Role::Use { &mut read } else { &mut written };
1488            into.push(name(operand));
1489        }
1490        (written, read)
1491    }
1492
1493    #[test]
1494    fn a_call_passes_its_arguments_where_the_convention_puts_them() {
1495        let i32 = Type::int(32);
1496        let (_, func, made) = make(&[i32, i32, i32], &[], false, &SYSV);
1497        assert_eq!(made.expect("three integers all fit in registers").results, []);
1498        assert_eq!(operands(&func).1, ["rdi", "rsi", "rdx"]);
1499    }
1500
1501    #[test]
1502    fn the_other_convention_passes_the_same_arguments_somewhere_else() {
1503        let i64 = Type::int(64);
1504        let (_, func, made) = make(&[i64, i64], &[], false, &WIN64);
1505        // Thirty two bytes of stack for a call that passes nothing on the stack, which is what
1506        // Windows asks a caller to leave the callee whether the callee uses it or not.
1507        assert_eq!(made.expect("two integers fit in registers").outgoing, 32);
1508        assert_eq!(operands(&func).1, ["rcx", "rdx"]);
1509    }
1510
1511    #[test]
1512    fn what_a_call_gives_back_comes_out_of_the_register_the_convention_returns_in() {
1513        let (names, func, made) = make(&[], &[Type::int(32)], false, &SYSV);
1514        let made = made.expect("an integer comes back");
1515        let [result] = made.results[..] else { panic!("one register") };
1516        // The first thing written is the result, and it is the only thing written that is a value
1517        // rather than a register the callee destroyed.
1518        assert_eq!(operands(&func).0.first().map(String::as_str), Some("rax"));
1519        assert_eq!(func.class_of(result), Some(SYSV.int_class));
1520        assert!(mir::print_func(&func, &names, &REGS).contains("x64.call"));
1521    }
1522
1523    #[test]
1524    fn every_register_the_callee_may_destroy_is_written_by_the_call() {
1525        let (_, func, _) = make(&[Type::int(64)], &[Type::int(64)], false, &SYSV);
1526        let (written, read) = operands(&func);
1527        // The callee saved registers are not here, because a value in one of those survives a
1528        // call and that is the whole difference between the two halves of the convention.
1529        for saved in ["rbx", "rbp", "r12", "r13", "r14", "r15"] {
1530            assert!(!written.contains(&saved.to_string()), "{saved} survives a call");
1531        }
1532        // Every other integer register is, once. The two named ones are named by the result and
1533        // by the argument instead, and naming one twice would be blocking it twice.
1534        for destroyed in ["rcx", "rdx", "rsi", "r8", "r9", "r10", "r11"] {
1535            let count = written.iter().filter(|name| *name == destroyed).count();
1536            assert_eq!(count, 1, "{destroyed} is destroyed by a call and is written {count} times");
1537        }
1538        assert_eq!(written.iter().filter(|name| *name == "rax").count(), 1);
1539        assert_eq!(read, ["rdi"]);
1540        // The vector registers are all destroyed on SysV, and they are in the other class.
1541        assert!(written.contains(&"xmm0".to_string()));
1542    }
1543
1544    #[test]
1545    fn a_variadic_call_says_how_many_vector_registers_it_passed_arguments_in() {
1546        let (names, func, made) = make(&[Type::int(64)], &[], true, &SYSV);
1547        made.expect("an integer argument to a variadic callee");
1548        let (_, read) = operands(&func);
1549        // Zero of them here, and `al` is where a SysV callee looks for it. Leaving whatever was in
1550        // the register there would make a callee that saves its vector registers save ones it was
1551        // never given.
1552        assert_eq!(read, ["rdi", "rax"]);
1553        assert_eq!(
1554            mir::print_func(&func, &names, &REGS).lines().nth(2),
1555            Some("    %1:gpr = x64.mov_ri_32 0")
1556        );
1557
1558        // Two of them here, which is the number that decides how much of the register save area a
1559        // callee like `printf` fills in. A count of zero with a float in `xmm0` would be a callee
1560        // reading its first `%f` out of a register nothing wrote.
1561        let f64 = Type::float(rucc_ir::Float::F64);
1562        let (names, func, made) = make(&[Type::int(64), f64, f64], &[], true, &SYSV);
1563        made.expect("one integer and two floats all fit in registers");
1564        assert_eq!(operands(&func).1, ["rdi", "xmm0", "xmm1", "rax"]);
1565        assert!(mir::print_func(&func, &names, &REGS).contains("x64.mov_ri_32 2"));
1566    }
1567
1568    /// Windows passes a float the callee has no prototype for in both files at once, because the
1569    /// callee has no way to know which file to look in and its walk over the arguments reads the
1570    /// general purpose one. This is the whole of what `printf("%f", x)` needs from the caller.
1571    #[test]
1572    fn a_float_a_variadic_callee_has_no_prototype_for_travels_in_both_files_on_windows() {
1573        let f64 = Type::float(rucc_ir::Float::F64);
1574        let (names, func, made) = made_naming(&[Type::int(32), f64], &[], 1, true, &WIN64);
1575        made.expect("an integer and a float both fit in registers");
1576
1577        // The float is the second argument, so its position is one and both of its registers are
1578        // the second of their file. `rdx` holds the bits and nothing converts them, which is what
1579        // the `movq` is: the callee reads bits out of it and not a value of any type.
1580        assert_eq!(operands(&func).1, ["rcx", "xmm1", "rdx"]);
1581        let text = mir::print_func(&func, &names, &REGS);
1582        assert!(text.contains("x64.movq_from_xmm %1"), "{text}");
1583    }
1584
1585    /// An argument the signature does name needs no second copy, since the callee's parameter says
1586    /// where it is, and neither does one on a convention that keeps the two files apart.
1587    #[test]
1588    fn an_argument_the_signature_names_travels_in_one_file() {
1589        let f64 = Type::float(rucc_ir::Float::F64);
1590        let (names, func, made) = made_naming(&[Type::int(32), f64], &[], 2, true, &WIN64);
1591        made.expect("both are named");
1592        assert_eq!(operands(&func).1, ["rcx", "xmm1"]);
1593        assert!(!mir::print_func(&func, &names, &REGS).contains("movq_from_xmm"));
1594
1595        let (names, func, made) = made_naming(&[Type::int(32), f64], &[], 1, true, &SYSV);
1596        made.expect("an integer and a float");
1597        assert!(!mir::print_func(&func, &names, &REGS).contains("movq_from_xmm"));
1598    }
1599
1600    /// A float past the position the registers run out at is in the argument area and nowhere else,
1601    /// which is where the second copy stops being a thing there is room for. The callee reads it
1602    /// out of memory whichever file it would have been in.
1603    #[test]
1604    fn a_float_the_registers_ran_out_before_gets_no_second_copy() {
1605        let f64 = Type::float(rucc_ir::Float::F64);
1606        let (names, func, made) = made_naming(&[f64; 6], &[], 0, true, &WIN64);
1607        made.expect("four in registers and two in memory");
1608        let text = mir::print_func(&func, &names, &REGS);
1609        assert_eq!(text.matches("movq_from_xmm").count(), 4, "{text}");
1610        assert!(text.contains("x64.movsd_mr %4, [$rsp + 32]"), "{text}");
1611    }
1612
1613    #[test]
1614    fn a_call_through_an_address_reads_it_in_front_of_the_arguments() {
1615        let i32 = Type::int(32);
1616        let mut names = Interner::new();
1617        let mut out = mir::Func::new(names.intern("f"));
1618        let block = out.create_block();
1619        let address = out.append_param(block, SYSV.int_class);
1620        let reg = out.append_param(block, SYSV.int_class);
1621        let passed = vec![Passing { ty: i32, reg, abi: Abi::Plain }];
1622        let what = Calling {
1623            callee: Callee::Through(address),
1624            args: &passed,
1625            returns: &[i32],
1626            variadic: false,
1627            named: passed.len(),
1628            at: Span::DUMMY,
1629        };
1630        call(&mut out, block, &what, &SYSV, &X86_64, &mut names)
1631            .expect("one integer fits in a register");
1632
1633        // The address is the first thing read and the arguments follow it, which is the order the
1634        // assembler counts on, and it is in no particular register because every register a call
1635        // could insist on is one the call has already spoken for.
1636        let text = mir::print_func(&out, &names, &REGS);
1637        assert!(text.contains("= x64.call_reg %0, %1($rdi)\n"), "{text}");
1638        assert!(!text.contains("@g"), "a call through an address names nobody: {text}");
1639    }
1640
1641    #[test]
1642    fn a_call_with_no_register_left_writes_the_argument_into_the_outgoing_area() {
1643        let i64 = Type::int(64);
1644        let (names, func, made) = make(&[i64; 7], &[], false, &SYSV);
1645        let made = made.expect("the seventh goes to memory");
1646
1647        // At the stack pointer, because the outgoing area is at the bottom of the frame, and in
1648        // front of the call rather than as an operand of it.
1649        let text = mir::print_func(&func, &names, &REGS);
1650        assert!(text.contains("x64.mov_mr_64 %6, [$rsp]\n"), "{text}");
1651        let store = text.find("x64.mov_mr_64").expect("the store");
1652        assert!(store < text.find("x64.call").expect("the call"), "{text}");
1653        // One word of it, which is what the frame has to reserve for this call.
1654        assert_eq!(made.outgoing, 8);
1655    }
1656
1657    /// One call to `g`, passing that many words, then an object of that size and alignment by
1658    /// value, then one more integer, which is `int g(long.., struct Big, int)` after the
1659    /// classification.
1660    fn pass_bytes(
1661        before: usize,
1662        size: u64,
1663        align: u32,
1664        conv: &CallRegs,
1665    ) -> (Interner, mir::Func, Result<Made, Refused>) {
1666        let mut names = Interner::new();
1667        let mut out = mir::Func::new(names.intern("f"));
1668        let block = out.create_block();
1669        let mut args: Vec<Passing> = (0..before)
1670            .map(|_| Passing {
1671                ty: Type::int(64),
1672                reg: out.append_param(block, conv.int_class),
1673                abi: Abi::Plain,
1674            })
1675            .collect();
1676        args.push(Passing {
1677            ty: Type::PTR,
1678            reg: out.append_param(block, conv.int_class),
1679            abi: Abi::ByVal { size, align, drains: Drains::Nothing },
1680        });
1681        args.push(Passing {
1682            ty: Type::int(32),
1683            reg: out.append_param(block, conv.int_class),
1684            abi: Abi::Plain,
1685        });
1686        let callee = Callee::Named(names.intern("g"));
1687        let what = Calling {
1688            callee,
1689            args: &args,
1690            returns: &[],
1691            variadic: false,
1692            named: args.len(),
1693            at: Span::DUMMY,
1694        };
1695        let made = call(&mut out, block, &what, conv, &X86_64, &mut names);
1696        (names, out, made)
1697    }
1698
1699    #[test]
1700    fn a_structure_passed_by_value_in_memory_is_copied_into_the_outgoing_area() {
1701        let (names, func, made) = pass_bytes(1, 24, 8, &SYSV);
1702        let made = made.expect("an object of three words is copied a word at a time");
1703
1704        // The bytes travel and the address does not, so the copy is a load and a store for each
1705        // word of it, in front of the call, and the callee's copy is at the bottom of the outgoing
1706        // area. The caller owes it this copy: the callee is free to write to what it was handed,
1707        // so what it was handed cannot be the object itself.
1708        let text = mir::print_func(&func, &names, &REGS);
1709        assert!(text.contains("x64.mov_mr_64 %3, [$rsp]\n"), "{text}");
1710        assert!(text.contains("x64.mov_mr_64 %4, [$rsp + 8]\n"), "{text}");
1711        assert!(text.contains("x64.mov_mr_64 %5, [$rsp + 16]\n"), "{text}");
1712        assert_eq!(text.matches("x64.mov_rm_64").count(), 3, "{text}");
1713        assert!(text.find("x64.mov_mr_64") < text.find("x64.call"), "{text}");
1714        assert_eq!(made.outgoing, 24);
1715    }
1716
1717    #[test]
1718    fn the_integers_beside_it_still_travel_in_registers() {
1719        let (_, func, _) = pass_bytes(1, 24, 8, &SYSV);
1720
1721        // An object in the argument area takes no argument register, so the integer behind it is
1722        // in the second one and not the third. Counting it as a register is the mistake that would
1723        // shift every argument after it along by one.
1724        let (clobbered, read) = operands(&func);
1725        assert_eq!(read, ["rdi", "rsi"]);
1726        assert!(clobbered.contains(&"rdx".to_owned()), "the third is free: {clobbered:?}");
1727    }
1728
1729    #[test]
1730    fn an_object_wanting_more_alignment_than_a_word_gets_it() {
1731        let (names, func, made) = pass_bytes(7, 24, 16, &SYSV);
1732        let made = made.expect("an object of three words");
1733
1734        // Six of the integers took the registers and the seventh is at the bottom of the area, so
1735        // the object cannot start where it left off: sixteen byte alignment moves it up to the
1736        // next multiple of sixteen and leaves a word of nothing behind it. The integer after it is
1737        // above all three of its words.
1738        let text = mir::print_func(&func, &names, &REGS);
1739        assert!(text.contains("x64.mov_mr_64 %9, [$rsp + 16]\n"), "{text}");
1740        assert!(text.contains("x64.mov_mr_32 %8, [$rsp + 40]\n"), "{text}");
1741        assert_eq!(made.outgoing, 48);
1742    }
1743
1744    #[test]
1745    fn an_object_too_large_to_copy_a_word_at_a_time_is_copied_by_the_runtime() {
1746        let (names, func, made) = pass_bytes(1, 4096, 8, &SYSV);
1747        let made = made.expect("an object the runtime copies");
1748
1749        // Five hundred and twelve words is past what unrolling is worth, so the copy is the call
1750        // the same size of `memcpy` in the IR becomes: the address in the outgoing area, the
1751        // address of the object, and the count, and then the call the object was an argument of.
1752        let text = mir::print_func(&func, &names, &REGS);
1753        assert!(text.contains("x64.lea_64 [$rsp]"), "{text}");
1754        assert!(text.contains("x64.mov_ri_32 4096"), "{text}");
1755        assert_eq!(text.matches("x64.call").count(), 2, "the copy and the call: {text}");
1756        assert!(text.find("@memcpy") < text.find("@g"), "the copy comes first: {text}");
1757
1758        // And the argument area is the object, because a call passing three words in registers
1759        // needs none of its own.
1760        assert_eq!(made.outgoing, 4096);
1761    }
1762
1763    #[test]
1764    fn an_object_too_large_to_count_the_bytes_of_is_reported_rather_than_passed() {
1765        let (_, _, made) = pass_bytes(1, 1 << 31, 8, &SYSV);
1766
1767        // Two gigabytes is more than the immediate the byte count travels in holds, and a count
1768        // that does not fit is the whole of what is left to refuse. No program passes a structure
1769        // that size by value, and one that tried would rather hear about it than be handed a copy
1770        // of the low part of it.
1771        assert_eq!(made, Err(Refused { argument: Some(1), missing: Missing::TooBig }));
1772        assert_eq!(
1773            Missing::TooBig.why(),
1774            "is more bytes than a count of them can be written down as"
1775        );
1776    }
1777
1778    /// An `ms_abi` function on Linux takes its arguments the Windows way: four in registers from
1779    /// `rcx`, and the fifth above the 32 bytes of shadow space.
1780    #[test]
1781    fn an_ms_abi_function_on_linux_takes_its_arguments_the_windows_way() {
1782        let ms = SYSV.under(Convention::Ms).expect("Linux has the Windows convention too");
1783        let (text, up) = arrive(&[Type::int(64); 7], ms);
1784        assert_eq!(up, [32, 40, 48]);
1785        assert!(text.contains("%0:gpr($rcx) = x64.arg_val_64"), "{text}");
1786        assert_eq!(text.matches("x64.arg_val_64").count(), 4, "{text}");
1787    }
1788
1789    /// A `sysv_abi` function on Windows takes six in registers from `rdi`, and the seventh at the
1790    /// bottom of the argument area with no shadow space under it.
1791    #[test]
1792    fn a_sysv_abi_function_on_windows_takes_its_arguments_the_system_v_way() {
1793        let sysv = WIN64.under(Convention::Sysv).expect("Windows has the System V convention too");
1794        let (text, up) = arrive(&[Type::int(64); 7], sysv);
1795        assert_eq!(up, [0]);
1796        assert!(text.contains("%0:gpr($rdi) = x64.arg_val_64"), "{text}");
1797        assert_eq!(text.matches("x64.arg_val_64").count(), 6, "{text}");
1798    }
1799
1800    /// A call to an `ms_abi` function from Linux leaves the callee its shadow space, and does not
1801    /// count `rsi`, `rdi` or the upper vector registers as destroyed, since the callee keeps them.
1802    #[test]
1803    fn a_call_to_an_ms_abi_function_keeps_what_the_callee_keeps() {
1804        let ms = SYSV.under(Convention::Ms).expect("Linux has the Windows convention too");
1805        let i64 = Type::int(64);
1806        let (_, func, made) = make(&[i64, i64], &[i64], false, ms);
1807        assert_eq!(made.expect("two integers fit in registers").outgoing, 32);
1808        let (written, read) = operands(&func);
1809        assert_eq!(read, ["rcx", "rdx"]);
1810        for kept in ["rsi", "rdi", "xmm6", "xmm15"] {
1811            assert!(!written.contains(&kept.to_string()), "{kept} survives the call");
1812        }
1813        assert!(written.contains(&"xmm5".to_string()), "{written:?}");
1814    }
1815
1816    /// A call to a `sysv_abi` function from Windows is the other way round: no shadow space, the
1817    /// arguments from `rdi`, and `rsi`, `rdi` and every vector register destroyed, which is what
1818    /// makes the calling Windows function save the ones it owes its own caller.
1819    #[test]
1820    fn a_call_to_a_sysv_abi_function_destroys_what_windows_would_keep() {
1821        let sysv = WIN64.under(Convention::Sysv).expect("Windows has the System V convention too");
1822        let i64 = Type::int(64);
1823        let (_, func, made) = make(&[i64, i64], &[i64], false, sysv);
1824        assert_eq!(made.expect("two integers fit in registers").outgoing, 0);
1825        let (written, read) = operands(&func);
1826        assert_eq!(read, ["rdi", "rsi"]);
1827        for destroyed in ["xmm6", "xmm15"] {
1828            assert!(written.contains(&destroyed.to_string()), "{destroyed}: {written:?}");
1829        }
1830        // `rsi` is named by an argument and so is not repeated as a clobber, and `rdi` likewise.
1831        assert!(!written.contains(&"rbx".to_string()), "{written:?}");
1832    }
1833
1834    /// The other convention runs out three arguments earlier and starts its argument area above the
1835    /// shadow space it also has to reserve, and both of those are what `Places` already said.
1836    #[test]
1837    fn where_the_outgoing_area_starts_is_the_convention_s_answer() {
1838        let i64 = Type::int(64);
1839        let (names, func, made) = make(&[i64; 7], &[], false, &WIN64);
1840        assert_eq!(made.expect("the last three go to memory").outgoing, 56);
1841
1842        // Thirty two bytes of shadow space first, which the caller writes nothing into and the
1843        // callee owns, and the fifth argument above it.
1844        let text = mir::print_func(&func, &names, &REGS);
1845        assert!(text.contains("x64.mov_mr_64 %4, [$rsp + 32]\n"), "{text}");
1846        assert!(text.contains("x64.mov_mr_64 %5, [$rsp + 40]\n"), "{text}");
1847        assert!(text.contains("x64.mov_mr_64 %6, [$rsp + 48]\n"), "{text}");
1848    }
1849
1850    /// What a stack argument is written with is its own width and its own register file, matching
1851    /// what the callee reads it back with.
1852    #[test]
1853    fn a_narrow_or_floating_argument_keeps_its_own_store() {
1854        let i64 = Type::int(64);
1855        let narrow = [i64, i64, i64, i64, i64, i64, Type::int(8)];
1856        let (names, func, made) = make(&narrow, &[], false, &SYSV);
1857        made.expect("the seventh goes to memory");
1858        let text = mir::print_func(&func, &names, &REGS);
1859        assert!(text.contains("x64.mov_mr_8 %6, [$rsp]\n"), "{text}");
1860
1861        let f32 = Type::float(rucc_ir::Float::F32);
1862        let (names, func, made) = make(&[f32; 9], &[], false, &SYSV);
1863        made.expect("the ninth goes to memory");
1864        let text = mir::print_func(&func, &names, &REGS);
1865        assert!(text.contains("x64.movss_mr %8, [$rsp]\n"), "{text}");
1866    }
1867
1868    /// The count a SysV variadic callee reads is a count of registers, so an argument that went to
1869    /// memory instead is not in it.
1870    #[test]
1871    fn an_argument_in_memory_is_not_counted_as_a_vector_register() {
1872        let f64 = Type::float(rucc_ir::Float::F64);
1873        let (names, func, made) = make(&[f64; 9], &[], true, &SYSV);
1874        made.expect("the ninth goes to memory");
1875        let text = mir::print_func(&func, &names, &REGS);
1876        assert!(text.contains("x64.mov_ri_32 8\n"), "eight registers, not nine: {text}");
1877    }
1878
1879    /// The two lists of widths answer for the same set of types, so that a value the callee can
1880    /// read out of the argument area is one the caller can write into it.
1881    #[test]
1882    fn what_can_be_read_can_be_written() {
1883        let types = [
1884            Type::int(1),
1885            Type::int(8),
1886            Type::int(16),
1887            Type::int(32),
1888            Type::int(64),
1889            Type::int(128),
1890            Type::PTR,
1891            Type::float(rucc_ir::Float::F32),
1892            Type::float(rucc_ir::Float::F64),
1893            Type::float(rucc_ir::Float::F80),
1894        ];
1895        for ty in types {
1896            assert_eq!(load_of(ty).is_some(), store_of(ty).is_some(), "{ty:?}");
1897        }
1898    }
1899
1900    /// A float travels in the other file at both ends of a call, and the register it comes back in
1901    /// is the first of that file rather than the first of the other one.
1902    #[test]
1903    fn a_call_passes_and_returns_a_float_in_a_vector_register() {
1904        let f64 = Type::float(rucc_ir::Float::F64);
1905        let (_, func, made) = make(&[Type::int(32), f64], &[f64], false, &SYSV);
1906        let result = made.expect("an integer and a float both fit in registers");
1907        let (written, read) = operands(&func);
1908        assert_eq!(read, ["rdi", "xmm0"]);
1909        assert_eq!(written.first().map(String::as_str), Some("xmm0"));
1910        assert_eq!(func.class_of(result.results[0]), Some(SYSV.sse_class));
1911        // Written once, because the register the result comes back in is already blocked by being
1912        // named and a clobber that repeated it would be blocking it twice. `rax` is a clobber here
1913        // rather than the result, which is the same register number in the other file and is the
1914        // whole reason the two lists are counted apart.
1915        assert_eq!(written.iter().filter(|name| *name == "xmm0").count(), 1);
1916        assert!(written.contains(&"rax".to_string()));
1917    }
1918
1919    #[test]
1920    fn a_call_at_a_width_no_register_holds_is_reported_on_either_side() {
1921        let i128 = Type::int(128);
1922        assert_eq!(
1923            make(&[i128], &[], false, &SYSV).2,
1924            Err(Refused { argument: Some(0), missing: Missing::Width })
1925        );
1926        assert_eq!(
1927            make(&[], &[i128], false, &SYSV).2,
1928            Err(Refused { argument: None, missing: Missing::Width })
1929        );
1930    }
1931
1932    #[test]
1933    fn an_argument_wider_than_a_register_has_no_name() {
1934        assert_eq!(head_of(Type::int(128)), None);
1935        assert_eq!(head_of(Type::int(8)), Some("x64.arg_val_8"));
1936        assert_eq!(head_of(Type::int(64)), Some("x64.arg_val_64"));
1937    }
1938
1939    /// An address arrives in a general purpose register like any other integer of its width, and
1940    /// used to be turned away here as a width no register holds, which is what issue 274 is.
1941    /// `int g(char *s)` is the smallest program that was.
1942    #[test]
1943    fn an_address_arrives_in_a_register_like_the_integer_it_is() {
1944        assert_eq!(head_of(Type::PTR), Some("x64.arg_val_64"));
1945        assert_eq!(
1946            bind(&[Type::PTR], &SYSV),
1947            "mfunc @f {\nblock0:\n    %0:gpr($rdi) = x64.arg_val_64\n}\n"
1948        );
1949        // And it travels the same way at a call, on both sides of one.
1950        assert!(make(&[Type::PTR], &[Type::PTR], false, &SYSV).2.is_ok());
1951    }
1952}