Skip to main content

rucc_asm/
bytes.rs

1//! Machine functions as the bytes of a text section.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.1. The other end of [`crate::att`], and
4//! deliberately the same walk: an opcode is the list of instructions the target says it is, each
5//! instruction's arguments are drawn from the operands the target says they come from, and the
6//! only difference is that this hands each one to the encoder instead of writing its name. That
7//! is what section 11.1 means by one description rather than two, and it is why a mistake here
8//! cannot be a mistake about what an instruction is. It can only be a mistake about bytes.
9//!
10//! # What the encoder cannot know
11//!
12//! Where anything outside the instruction is. A jump carries the distance to its target and the
13//! target is a block that may not have been written yet, and a call carries the distance to a
14//! function that is not in this file at all. The encoder leaves four bytes for each and says
15//! where it left them, and this fills in the ones it can and records the ones it cannot.
16//!
17//! The ones it can are the jumps inside a function, since by the end of a function every block
18//! has a place. They are patched here and nothing downstream ever hears about them.
19//!
20//! The ones it cannot are the references to a symbol, which are a relocation: an offset into the
21//! section, the name of the thing wanted, and what the linker is being asked for. Choosing which
22//! relocation goes with which addressing mode is this layer's job rather than the object writer's,
23//! per section 11.3, because it is a fact about the instruction and not about the file format.
24//!
25//! # What is not decided here
26//!
27//! How long a jump is. Every one of them takes four bytes for its distance whether it needs them
28//! or not, which is correct and larger than it has to be. Shrinking the ones that fit in a byte is
29//! relaxation, an iterate-to-fixpoint pass over the whole function, and it is not written yet.
30//! Nothing here would have to change for it: it would run before this and settle the lengths.
31//!
32//! Alignment between functions, beyond starting each one on a sixteen byte boundary, which is what
33//! every x86-64 toolchain does and what the instruction fetcher is built around. The padding is
34//! written as single byte nops. A longer nop is fewer instructions to decode and the padding
35//! between two functions is never executed, so there is nothing to be gained by it.
36
37use std::collections::HashMap;
38
39use rucc_base::Interner;
40use rucc_diag::Span;
41use rucc_mir::{Amode, Block, Func, Inst, Operand, Reach, defs};
42use rucc_target::x86_64::{self, Addr, Arg, RAX, Value, Width};
43use rucc_target::{ObjectFormat, PhysReg, TargetInfo};
44use rucc_tuple::Arch;
45
46use rucc_object::{
47    Binding, Chunk, Extent, FUNC_ALIGN, Held, Marker, Patch, Reference, Reloc, Site, Table, Text,
48    Visibility,
49};
50
51use crate::Error;
52use crate::format::{Directives, binding, visibility};
53use crate::unwind::{self, Rows};
54
55/// The prefix every x86-64 opcode carries in the machine IR.
56const PREFIX: &str = "x64.";
57
58/// The one byte instruction that does nothing, which is what the space in front of a function is.
59///
60/// Also what the room a patcher was promised is made of. The two are the same byte and not the same
61/// thing: the padding is space nothing reaches, and the room is space something jumps into once it
62/// has been written over. See `assemble`.
63const NOP: u8 = 0x90;
64
65/// Where one machine instruction ended up, and where in the source it came from.
66///
67/// The span rather than a file and a line, because this layer has no source map and no business
68/// acquiring one. Turning a span into a place is the driver's, which is also where the paths a
69/// `-ffile-prefix-map` rewrites are still paths.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct Row {
72    /// How far into its own function the instruction begins.
73    pub at: usize,
74    /// What the machine IR said this instruction was for.
75    pub span: Span,
76    /// Which instruction of the machine function it is, or `None` for the row the prologue gets,
77    /// which is the one row here that no instruction wrote.
78    ///
79    /// The line table has no use for it and the locations do: a local the allocator kept in a
80    /// register is somewhere over a stretch the back end named by an instruction at each end,
81    /// because a machine instruction has no length until something encodes it, and this is where
82    /// it gets one. Carried on the row rather than as a second list because the two are the same
83    /// walk and a second list is a thing that can come to disagree with the first.
84    pub inst: Option<Inst>,
85}
86
87/// A text section and, when the build asked for it, where each instruction in it came from.
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct Assembled {
90    /// The instructions, and what the linker has to be told about them.
91    pub text: Text,
92    /// One list per function of [`Text::funcs`], in the same order, and empty throughout in a
93    /// build that asked for no debug information.
94    pub lines: Vec<Vec<Row>>,
95    /// The frame rules as `.debug_frame`, in a build that asked for debug information and for no
96    /// unwind table, where it is the only table a debugger has to find a frame base through. None
97    /// in every other build, and on a format that has no such section.
98    pub frames: Option<Chunk>,
99}
100
101/// Every function, as the bytes of a text section.
102///
103/// `unwind` is whether a function is described to an unwinder, which is
104/// `rucc_session::Options::unwinds` and is asked of the build rather than worked out here, so that
105/// this and the text writer cannot answer it differently for one function.
106///
107/// `lines` is whether to record where each instruction came from, which is
108/// `rucc_session::Options::debug_info` and is asked the same way and for the same reason. It is a
109/// question rather than something always answered because the rows are one per machine instruction
110/// and a build that is not writing debug information would carry them the length of the back end to
111/// throw them away.
112///
113/// # Errors
114///
115/// [`Error::Machine`] for an architecture nothing here encodes, and the rest for a function that
116/// should not have got this far. See [`Error`].
117///
118/// # Panics
119///
120/// Panics on a function that was promised room for a patcher and has none on either side of its
121/// own label, which is a prologue that recorded room it did not write.
122pub fn assemble(
123    funcs: &[Func],
124    names: &Interner,
125    target: &TargetInfo,
126    unwind: bool,
127    lines: bool,
128) -> Result<Assembled, Error> {
129    if target.tuple.arch() != Arch::X86_64 {
130        return Err(Error::Machine { triple: target.tuple.to_string() });
131    }
132    let mut text = Text::default();
133    let mut all = Vec::new();
134    // Where each function's frame rules landed, kept beside the extents rather than written into
135    // the section as they are found, because a record counts from the start of its function and the
136    // function's own length is not known until its last instruction has been encoded.
137    let mut rows = Vec::with_capacity(funcs.len());
138    for func in funcs {
139        // What this function asked for, which pads the space in front of it and, once every
140        // function has been through here, is what the whole section is aligned to. Both halves
141        // are needed: the offset inside the section is this padding and where the section itself
142        // lands is the alignment recorded on it. It goes on the extent as well, because under
143        // `-ffunction-sections` this function is a section of its own and the padding in front of
144        // it is gone, so this number is the only thing left saying what it wanted.
145        let align = func.align.unwrap_or(FUNC_ALIGN);
146        text.align = text.align.max(align);
147        let step = usize::try_from(align).unwrap_or(1).max(1);
148        while text.bytes.len() % step != 0 {
149            text.bytes.push(NOP);
150        }
151        // The half of the room a patcher was promised that is in front of the function's own
152        // label, laid down here because it is the one part of a finished function that is not in a
153        // block. What makes it the space in front of the function rather than the start of it is
154        // everything below: the symbol, the size and the record an unwinder reads all begin after
155        // it, which is what gcc does with the same flag and what a debugger showing a backtrace
156        // through a patched function needs.
157        //
158        // The byte is written rather than encoded because the room is counted in bytes and the
159        // instruction that fills it has no operands. `an_entry_promised_to_a_patcher_is_bytes_that
160        // _do_nothing_on_both_sides_of_the_symbol` is what holds it to the same byte the encoder
161        // writes for the half that is in a block.
162        let ahead = text.bytes.len();
163        if let Some(patch) = func.patch {
164            text.bytes.extend(std::iter::repeat_n(NOP, patch.before as usize));
165        }
166        let start = text.bytes.len();
167        let name = names.resolve(func.name).to_owned();
168        let mut assembler = Assembler {
169            names,
170            directives: Directives::of(target.object_format),
171            func,
172            name: &name,
173            text: &mut text,
174            blocks: Vec::new(),
175            jumps: Vec::new(),
176            rows: Vec::new(),
177            lines: Vec::new(),
178            wants: lines,
179            start,
180            room: None,
181            loops: Vec::new(),
182            apart: target.object_format == ObjectFormat::Elf,
183            sites: Vec::new(),
184        };
185        assembler.func()?;
186        let room = assembler.room;
187        let blocks = std::mem::take(&mut assembler.blocks);
188        let mut landings: Vec<Site> = std::mem::take(&mut assembler.sites)
189            .into_iter()
190            .map(|(at, end, pad)| Site {
191                start: at,
192                len: end - at,
193                pad: blocks[pad.index()] - start,
194            })
195            .collect();
196        landings.sort_by_key(|site| site.start);
197        rows.push(std::mem::take(&mut assembler.rows));
198        all.push(std::mem::take(&mut assembler.lines));
199        let len = text.bytes.len() - start;
200        // Where the record points is the front of the room, which is the half in front of the
201        // label in a function that has one and the first instruction of the other half otherwise.
202        // The two are not one offset because a landing pad can sit between the halves.
203        let patch = func.patch.map(|patch| {
204            let at = if patch.before > 0 {
205                ahead
206            } else {
207                room.expect("room that is neither in front of the label nor anywhere after it")
208            };
209            Patch { at, before: patch.before as usize }
210        });
211        text.funcs.push(Extent {
212            name,
213            start,
214            len,
215            align,
216            binding: binding(func.binding),
217            visibility: visibility(func.visibility),
218            patch,
219            landings,
220        });
221    }
222    // In whichever of the two shapes the target reads, which is what decides whether a prologue
223    // this cannot describe is a refusal or is nothing at all. See [`unwind::table`].
224    // Or, when there is to be no unwind table and there is to be debug information, the same rows
225    // where only a debugger looks. See [`unwind::debug_frame`].
226    let mut frames = None;
227    if let Some(conv) = target.call_regs {
228        if unwind {
229            text.unwind = unwind::table(&text.funcs, &rows, conv, target.object_format)?;
230        } else if lines {
231            frames = unwind::debug_frame(&text.funcs, &rows, conv, target.object_format);
232        }
233    }
234    Ok(Assembled { text, lines: all, frames })
235}
236
237/// A template kept as text, as the bytes the assembler reads out of it on its own and the places in
238/// them that name something outside it, counted from the front of the template.
239///
240/// Read on its own when nothing in it reaches past its own text: no second section, no alignment,
241/// which counts from the front of a section this is not the front of, and no name it defines, since
242/// another template may be the one that jumps to it and the two are only put together in a
243/// listing. A numbered label it writes and goes to itself is a place rather than a name, and the
244/// reader has already turned every jump to one into a distance. What it names and does not define
245/// is left for the linker, the way gas would leave it, except for a local name, which is always in
246/// the same file and so is another template's. Anything else is an error with what about the text
247/// it was, and the unit goes to the assembler as a listing instead.
248pub(crate) fn template(
249    func: &Func,
250    block: Block,
251    inst: Inst,
252    names: &Interner,
253    directives: Directives,
254) -> Result<(Vec<u8>, Vec<Reloc>), String> {
255    // On a format whose names carry a prefix the text and the linker spell a name differently, and
256    // which one a name in the template meant is not a question this can answer.
257    if !directives.symbol().is_empty() {
258        return Err("names on this format carry a prefix".to_owned());
259    }
260    let text = crate::att::template(func, block, inst, names, directives)
261        .map_err(|trouble| trouble.to_string())?;
262    let read = crate::source::read(&format!("{}\n{text}", directives.text()), Arch::X86_64)
263        .map_err(|trouble| trouble.why)?;
264    for name in &read.names {
265        let outside = name.at == Held::Undefined
266            && name.binding == Binding::Global
267            && name.visibility == Visibility::Default
268            && !name.name.starts_with(directives.local());
269        if !outside {
270            return Err(format!("it names '{}' in a way only the whole file can say", name.name));
271        }
272    }
273    match read.parts.as_slice() {
274        [] => Ok((Vec::new(), Vec::new())),
275        [part] if part.name == ".text" && part.align <= 1 => {
276            Ok((part.bytes.clone(), part.relocs.clone()))
277        }
278        _ => Err("it writes into a section of its own or aligns what follows".to_owned()),
279    }
280}
281
282/// A jump inside a function, waiting for the block it goes to to have a place.
283struct Jump {
284    /// Where the four bytes the distance goes in begin.
285    at: usize,
286    /// Where the instruction it belongs to ends, which is what the distance is counted from.
287    end: usize,
288    /// The place it goes to.
289    to: To,
290    /// What is added to the distance, which is nothing for a jump and is the displacement for an
291    /// address that names a block and has one.
292    disp: i64,
293}
294
295/// A place in this function that an instruction can name: a block, or one of its jump tables.
296#[derive(Clone, Copy)]
297enum To {
298    Block(Block),
299    Table(u32),
300}
301
302/// One function being written out.
303struct Assembler<'a> {
304    names: &'a Interner,
305    /// How the listing spells things, which a template kept as text is filled in with before it is
306    /// read. See [`template`].
307    directives: Directives,
308    func: &'a Func,
309    name: &'a str,
310    text: &'a mut Text,
311    /// Where each block starts, indexed by the block's own number, or [`usize::MAX`] for one that
312    /// is not in the layout.
313    blocks: Vec<usize>,
314    jumps: Vec<Jump>,
315    /// The frame rules, each with how far into this function the instruction that changed them
316    /// ended.
317    rows: Rows,
318    /// Where each machine instruction began and what it was for, in the order they were written.
319    ///
320    /// Empty in a build that asked for no debug information, which is what `wants` says.
321    lines: Vec<Row>,
322    /// Whether to fill `lines` in at all.
323    wants: bool,
324    /// Where this function starts in the section, which is what those distances are counted from.
325    start: usize,
326    /// Where the room a patcher was promised after the label began, which is where the instruction
327    /// [`rucc_mir::Patch::after`] names was encoded.
328    ///
329    /// [`None`] in a function that was promised none and in one whose room is all in front of the
330    /// label, which is the same answer to two different questions and is why the caller decides
331    /// which of them it asked. See `assemble`.
332    room: Option<usize>,
333    /// How long the loop each block is the head of is, indexed by the block's own number, and zero
334    /// for a block that heads none. See [`loop_sizes`].
335    loops: Vec<usize>,
336    /// Whether the jump tables go in `.rodata` rather than after the code, which they do on ELF.
337    /// See [`Self::tables`].
338    apart: bool,
339    /// Where each call an unwind lands from began and ended, counted from the front of the
340    /// function, with the pad it lands in. See [`rucc_mir::Func::landings`].
341    sites: Vec<(usize, usize, Block)>,
342}
343
344/// How long each loop in the function is, from its head to the end of the last jump back to it,
345/// indexed by the head's own number and zero for a block that is not a head.
346///
347/// Worked out by laying the function out once with no padding and throwing the bytes away. That is
348/// exact because every jump here is four bytes of distance whatever the distance is, so no
349/// instruction's length depends on where it lands and padding in front of the head moves the whole
350/// loop without changing its size. The cost is encoding a function twice, and only a function
351/// something asked to pad a loop in pays it.
352pub(crate) fn loop_sizes(
353    names: &Interner,
354    directives: Directives,
355    func: &Func,
356) -> Result<Vec<usize>, Error> {
357    let mut sizes = vec![0; func.block_count()];
358    if func.heads.is_empty() {
359        return Ok(sizes);
360    }
361    let mut text = Text::default();
362    let mut scratch = Assembler {
363        names,
364        directives,
365        func,
366        name: "",
367        text: &mut text,
368        blocks: Vec::new(),
369        jumps: Vec::new(),
370        rows: Vec::new(),
371        lines: Vec::new(),
372        wants: false,
373        start: 0,
374        room: None,
375        loops: Vec::new(),
376        apart: false,
377        sites: Vec::new(),
378    };
379    scratch.lay()?;
380    for jump in &scratch.jumps {
381        let To::Block(head) = jump.to else { continue };
382        let start = scratch.blocks[head.index()];
383        // A jump that ends in front of the head is the way into the loop and not the way round it.
384        if start == usize::MAX || jump.end <= start || !func.heads.contains(&head) {
385            continue;
386        }
387        sizes[head.index()] = sizes[head.index()].max(jump.end - start);
388    }
389    Ok(sizes)
390}
391
392impl Assembler<'_> {
393    /// The blocks, and then the jumps between them once every block has a place.
394    fn func(&mut self) -> Result<(), Error> {
395        self.loops = loop_sizes(self.names, self.directives, self.func)?;
396        self.lay()?;
397        let tables = self.tables()?;
398        self.patch(&tables)
399    }
400
401    /// The blocks, one after another, with the jumps between them left for [`Self::patch`].
402    fn lay(&mut self) -> Result<(), Error> {
403        self.blocks = vec![usize::MAX; self.func.block_count()];
404        // The prologue, first, because nothing in it has a span of its own. The pushes, the frame
405        // and the moves that put the arguments where the body expects them came from no expression
406        // in the source, so without this the front of every function is the one part of it no row
407        // covers, and a program counter in there gets no answer at all rather than a slightly
408        // early one. Where the function was declared is what gcc says over those bytes.
409        if self.wants && !self.func.declared.is_dummy() {
410            self.lines.push(Row { at: 0, span: self.func.declared, inst: None });
411        }
412        let end = self.func.cfi_end();
413        self.sites.clear();
414        let pads: HashMap<Inst, Block> = self.func.landings.iter().copied().collect();
415        for block in self.func.blocks() {
416            // The head of a loop is padded the way the listing asks the assembler to pad it, with
417            // instructions rather than single bytes, since the block in front of it may fall in.
418            // The section is told for the reason an alignment instruction tells it below, since a
419            // place inside a line of the section is one inside a line of memory only if the
420            // section starts on one.
421            let size = self.loops.get(block.index()).copied().unwrap_or(0);
422            if crate::loop_room(size).is_some() {
423                let count = crate::loop_padding(self.text.bytes.len(), size);
424                x86_64::nops(count, &mut self.text.bytes);
425                self.text.align = self.text.align.max(crate::LINE as u32);
426            }
427            self.blocks[block.index()] = self.text.bytes.len();
428            // And the name an image knows the block by, as a symbol at the same byte. The number
429            // the jumps above use is worked out here and stays here, because both ends of a jump
430            // are in this section. An image is in another one, so what it holds is a relocation
431            // and a relocation names a symbol, which is what this is.
432            if let Some(label) = self.func.block_name(block) {
433                let name = self.names.resolve(label).to_owned();
434                self.text.labels.push(Marker { name, at: self.text.bytes.len() });
435            }
436            for inst in self.func.insts(block) {
437                // Before it is encoded, because what is wanted is where it begins and after this
438                // it has already been written. A landing pad is in front of it in a function that
439                // has one, which is why the room is found this way rather than measured from the
440                // top of the function.
441                if self.func.patch.is_some_and(|patch| patch.after == Some(inst)) {
442                    self.room = Some(self.text.bytes.len());
443                }
444                // Where it begins rather than where it ends, which is the other way round from the
445                // frame rules below and for the same reason they are that way round: a debugger is
446                // asking what a program counter is in the middle of, and an unwinder is asking what
447                // the frame looked like at a return address.
448                if self.wants {
449                    let at = self.text.bytes.len() - self.start;
450                    self.lines.push(Row { at, span: self.func.span(inst), inst: Some(inst) });
451                }
452                let began = self.text.bytes.len() - self.start;
453                self.inst(block, inst)?;
454                // A call an unwind lands somewhere from, as the bytes it is. See [`Site`].
455                if let Some(&pad) = pads.get(&inst) {
456                    self.sites.push((began, self.text.bytes.len() - self.start, pad));
457                }
458                if Some(inst) == end {
459                    continue;
460                }
461                // Where the instruction ended, because a row takes effect after the instruction
462                // that changed the answer and an unwinder is looking up a return address, which is
463                // the byte after a call rather than the call itself.
464                let at = self.text.bytes.len() - self.start;
465                self.rows.extend(self.func.cfi_after(inst).map(|op| (at, op)));
466            }
467        }
468        Ok(())
469    }
470
471    /// Where the jumps go, now that every block and every table has a place.
472    fn patch(&mut self, tables: &[usize]) -> Result<(), Error> {
473        for jump in std::mem::take(&mut self.jumps) {
474            let to = match jump.to {
475                To::Block(block) => self.blocks[block.index()],
476                To::Table(table) => tables[table as usize],
477            };
478            debug_assert_ne!(to, usize::MAX, "a jump to a block that was never laid out");
479            let distance = i64::try_from(to).expect("a section this size") + jump.disp
480                - i64::try_from(jump.end).expect("a section this size");
481            let distance = i32::try_from(distance)
482                .map_err(|_| Error::Distance { func: self.name.to_owned(), bytes: distance })?;
483            self.text.bytes[jump.at..jump.at + 4].copy_from_slice(&distance.to_le_bytes());
484        }
485        Ok(())
486    }
487
488    /// The jump tables, giving back where each one starts when it is in these bytes.
489    ///
490    /// On ELF each goes in `.rodata`, which is where gcc and clang put one: a table is read and
491    /// never run, and in the code it takes room in the lines the instruction fetcher reads and is
492    /// counted as code by anything that measures a section. What goes to the writer is which block
493    /// each cell names, counted from the front of the function, and the writer makes each cell a
494    /// relocation, since its two ends are no longer in one section. See [`Table`].
495    ///
496    /// On the other formats the table stays after the last instruction, where every cell is a
497    /// distance from the table to a block with both ends in this section, so the whole table is
498    /// filled in here and the linker is told nothing. The cells are four bytes each and start on a
499    /// four byte boundary, reached by the byte that does nothing, although nothing ever runs into
500    /// it: the last instruction of a function is a return or a jump.
501    fn tables(&mut self) -> Result<Vec<usize>, Error> {
502        let mut starts = Vec::with_capacity(self.func.tables.len());
503        if self.func.tables.is_empty() {
504            return Ok(starts);
505        }
506        if self.apart {
507            for (index, table) in self.func.tables.iter().enumerate() {
508                let block =
509                    self.func.block_of(table.jump).expect("a table read by a jump in no block");
510                let succs = &self.func[block].succs;
511                let cells = table
512                    .cells
513                    .iter()
514                    .map(|&cell| {
515                        let to = self.blocks[succs[cell as usize].block.index()];
516                        debug_assert_ne!(to, usize::MAX, "a table naming a block never laid out");
517                        to - self.start
518                    })
519                    .collect();
520                let name = self.table(index);
521                self.text.tables.push(Table { name, func: self.text.funcs.len(), cells });
522            }
523            return Ok(starts);
524        }
525        while self.text.bytes.len() % 4 != 0 {
526            self.text.bytes.push(NOP);
527        }
528        for table in &self.func.tables {
529            let start = self.text.bytes.len();
530            starts.push(start);
531            let block = self.func.block_of(table.jump).expect("a table read by a jump in no block");
532            let succs = &self.func[block].succs;
533            for &cell in &table.cells {
534                let to = self.blocks[succs[cell as usize].block.index()];
535                debug_assert_ne!(to, usize::MAX, "a table naming a block that was never laid out");
536                let distance = i64::try_from(to).expect("a section this size")
537                    - i64::try_from(start).expect("a section this size");
538                let distance = i32::try_from(distance)
539                    .map_err(|_| Error::Distance { func: self.name.to_owned(), bytes: distance })?;
540                self.text.bytes.extend_from_slice(&distance.to_le_bytes());
541            }
542        }
543        Ok(starts)
544    }
545
546    /// The name one jump table of this function goes by, which is the one the listing gives it.
547    fn table(&self, index: usize) -> String {
548        format!("{}{}_j{index}", self.directives.local(), self.name)
549    }
550
551    /// One instruction of the machine IR, as however many instructions of the machine it is.
552    fn inst(&mut self, block: Block, inst: Inst) -> Result<(), Error> {
553        let data = self.func[inst];
554        let spelled = self.names.resolve(data.opcode.name());
555        let opcode = spelled.strip_prefix(PREFIX).unwrap_or(spelled);
556        // The one opcode that is not an instruction. Where the listing writes the assembler's own
557        // directive this has to do what the assembler would have done, which is pad up to the
558        // boundary with the byte that does nothing, since the gap is reached by falling into it.
559        //
560        // The section has to be told as well. The padding puts the next instruction at a multiple of
561        // the boundary counted from the front of the section, and what makes that an address the
562        // program sees is the section itself landing on one, so the boundary goes on the section's
563        // alignment the way a function's own does.
564        if opcode == x86_64::ALIGN {
565            let bytes = data.imm.map_or(0, |imm| self.func[imm].0);
566            let boundary = u32::try_from(bytes).ok().filter(|at| at.is_power_of_two());
567            let Some(boundary) = boundary else {
568                return Err(Error::Opcode {
569                    func: self.name.to_owned(),
570                    opcode: spelled.to_owned(),
571                });
572            };
573            self.text.align = self.text.align.max(boundary);
574            let step = boundary as usize;
575            while self.text.bytes.len() % step != 0 {
576                self.text.bytes.push(NOP);
577            }
578            return Ok(());
579        }
580        // The other one, which is the bytes a template wrote out as themselves. There is nothing to
581        // encode: the program already said what the processor is to be handed, so they go down as
582        // they are.
583        if opcode == x86_64::LITERAL {
584            let Some(imm) = data.imm else {
585                return Err(Error::Opcode {
586                    func: self.name.to_owned(),
587                    opcode: spelled.to_owned(),
588                });
589            };
590            let before = self.text.bytes.len();
591            self.text.bytes.extend(x86_64::unpacked(self.func[imm].0));
592            if self.text.bytes.len() == before {
593                return Err(Error::Opcode {
594                    func: self.name.to_owned(),
595                    opcode: spelled.to_owned(),
596                });
597            }
598            return Ok(());
599        }
600        // A template kept as text, read on its own and laid down as what it came to. One the
601        // reader cannot take on its own sends the whole unit to the assembler as a listing instead
602        // and never comes here, see [`crate::kept`], so a refusal here is that check and this one
603        // disagreeing.
604        if opcode == x86_64::TEMPLATE {
605            let (bytes, relocs) =
606                template(self.func, block, inst, self.names, self.directives).map_err(|why| {
607                    Error::Encode { func: self.name.to_owned(), opcode: spelled.to_owned(), why }
608                })?;
609            let at = self.text.bytes.len();
610            self.text.bytes.extend(bytes);
611            self.text
612                .relocs
613                .extend(relocs.into_iter().map(|reloc| Reloc { at: reloc.at + at, ..reloc }));
614            return Ok(());
615        }
616        let Some(written) = x86_64::written(opcode) else {
617            return Err(Error::Opcode { func: self.name.to_owned(), opcode: spelled.to_owned() });
618        };
619        let operands = &self.func[data.operands];
620        for machine in written {
621            // What each argument turned out to be, and what the encoder has to be told about
622            // afterwards for the ones that name something it cannot see.
623            let mut values = Vec::with_capacity(machine.args.len());
624            let mut wanted = None;
625            // The other thing an address can name, which is a place in this same function and so is
626            // a distance nothing outside the file has to be told about.
627            let mut labelled = None;
628            for arg in machine.args {
629                values.push(match *arg {
630                    Arg::Reg(at, width) => {
631                        Value::Reg(self.phys(operands[usize::from(at)], spelled)?, width)
632                    }
633                    // The same thing in the other file, which the encoder has to be told apart
634                    // from the one above: which file a register is in is part of which instruction
635                    // it is, and the table it looks a row up in is what says so.
636                    Arg::Xmm(at) => Value::Xmm(self.phys(operands[usize::from(at)], spelled)?),
637                    // The two halves of one word. The encoder numbers a high byte as the low one
638                    // plus four, which is the whole of the difference between them in the bytes
639                    // and is also why only the first four registers have one.
640                    Arg::Low(at) => {
641                        Value::Reg(self.phys(operands[usize::from(at)], spelled)?, Width::Byte)
642                    }
643                    Arg::High(at) => Value::High(self.phys(operands[usize::from(at)], spelled)?),
644                    // The only register named outright on this machine is the high half of the
645                    // first one, which an eight bit remainder comes back in.
646                    Arg::Named(_) => Value::High(RAX),
647                    // A depth on the x87 stack, which carries nothing across because there is
648                    // nothing to carry: the depth is in the opcode byte the mnemonic picks, so
649                    // what the encoder needs from here is that an argument was there at all.
650                    Arg::Stack(_) => Value::Stack,
651                    Arg::Lit(lane) => Value::Imm(i64::from(lane)),
652                    // The first operand read, which is where a call puts the address it goes
653                    // through. Everything in front of it is a register the call writes.
654                    Arg::Through => {
655                        Value::Reg(self.phys(operands[defs(operands)], spelled)?, Width::Quad)
656                    }
657                    Arg::Imm => Value::Imm(data.imm.map_or(0, |imm| self.func[imm].0)),
658                    Arg::Mem => {
659                        let amode = data.mem.map(|mem| self.func[mem]);
660                        let (addr, symbol) = self.addr(operands, amode.as_ref(), spelled)?;
661                        if let Some(symbol) = symbol {
662                            // A mode that reads the global offset table names the slot rather than
663                            // the thing, and the four bytes are the same four bytes either way, so
664                            // which relocation it is is the whole of the difference here.
665                            let kind = match amode.map_or(Reach::Itself, |mem| mem.reach) {
666                                Reach::Itself => Reference::Data,
667                                Reach::Table => Reference::Got,
668                                Reach::Thread => Reference::Thread,
669                                Reach::Section => Reference::Section,
670                            };
671                            wanted = Some((symbol, kind, i64::from(addr.disp)));
672                        }
673                        if let Some(block) = amode.and_then(|mem| mem.block) {
674                            labelled = Some((To::Block(block), i64::from(addr.disp)));
675                        }
676                        if let Some(table) = amode.and_then(|mem| mem.table) {
677                            // In another section, so the linker's to fill in like any symbol.
678                            if self.apart && addr.rip {
679                                let name = self.table(table as usize);
680                                wanted = Some((name, Reference::Data, i64::from(addr.disp)));
681                            } else {
682                                labelled = Some((To::Table(table), i64::from(addr.disp)));
683                            }
684                        }
685                        Value::Mem(addr)
686                    }
687                    Arg::Symbol => {
688                        let symbol =
689                            data.symbol.map(|symbol| self.names.resolve(symbol).to_owned());
690                        if let Some(symbol) = symbol {
691                            wanted = Some((symbol, Reference::Call, 0));
692                        }
693                        Value::Dest
694                    }
695                    // Where a conditional jump goes is the first arm, because the block layout
696                    // guarantees the second is the block laid out next and is fallen into.
697                    Arg::Label => Value::Dest,
698                });
699            }
700
701            let start = self.text.bytes.len();
702            let holes =
703                x86_64::encode(machine.mnemonic, &values, &mut self.text.bytes).map_err(|why| {
704                    Error::Encode {
705                        func: self.name.to_owned(),
706                        opcode: spelled.to_owned(),
707                        why: why.to_string(),
708                    }
709                })?;
710            let end = self.text.bytes.len();
711
712            // A hole is either something outside the file, which is a relocation, or a block of
713            // this function, which is patched once every block has a place.
714            if let Some((symbol, kind, disp)) = wanted {
715                let kind = match kind {
716                    Reference::Got => slot(&self.text.bytes[start..end]),
717                    kind => kind,
718                };
719                let at = match kind {
720                    Reference::Call => holes.dest,
721                    Reference::Data
722                    | Reference::Got
723                    | Reference::GotBare
724                    | Reference::GotKept
725                    | Reference::Thread => holes.rip,
726                    Reference::Section => holes.disp,
727                    // An address written into an image rather than reached by an instruction, and
728                    // how far something is from the front of one, which is what a table of data
729                    // holds. Nothing above produces either, because every reference an instruction
730                    // makes is a distance from where the instruction ends. The field of an AArch64
731                    // instruction is not something this machine has.
732                    Reference::Address { .. }
733                    | Reference::Image
734                    | Reference::Away
735                    | Reference::Field(_) => {
736                        unreachable!("an instruction wanting an address")
737                    }
738                };
739                let at = at.expect("an instruction naming a symbol leaves room for the distance");
740                let addend = match kind {
741                    // Counted from the front of the section, so nothing about where the
742                    // instruction ends comes into it.
743                    Reference::Section => disp,
744                    _ => disp - i64::try_from(end - at).expect("an instruction this long"),
745                };
746                // How many bytes of the instruction come after the four the linker writes over,
747                // which is what is left of the distance from the hole to the end of it. Already in
748                // the addend and written down again because COFF wants the two apart, and there is
749                // nowhere else it can be worked out: by the time a writer sees the relocation the
750                // instruction it is in is bytes like any others.
751                let after = u8::try_from(end - at - 4).expect("an instruction this long");
752                self.text.relocs.push(Reloc { at, symbol, kind, addend, after });
753                // The addend is the whole of it, so the four bytes are left as nothing, which is
754                // what gas leaves. tcc's linker adds to what is there rather than writing over it,
755                // and a `mov cstr_buf+8(%rip)` with the eight in both places read eight bytes
756                // past the member it wanted.
757                self.text.bytes[at..at + 4].fill(0);
758            } else if let Some((to, disp)) = labelled {
759                // The address of a label, which is the four bytes an address counted from the
760                // instruction pointer leaves and is patched where a jump is patched rather than
761                // written out as a relocation, since both ends of it are in this function.
762                let at = holes.rip.expect("an address naming a label leaves room for the distance");
763                self.jumps.push(Jump { at, end, to, disp });
764            } else if let Some(at) = holes.dest {
765                match self.func[block].succs.first() {
766                    Some(call) => {
767                        self.jumps.push(Jump { at, end, to: To::Block(call.block), disp: 0 });
768                    }
769                    None => debug_assert!(false, "a jump out of a block with no arms"),
770                }
771            }
772        }
773        Ok(())
774    }
775
776    /// One address, with the operands it names resolved and the symbol it names handed back.
777    ///
778    /// A symbol with no base and no index is reached from the instruction pointer, which is how a
779    /// global is reached in position independent code and the only way this compiler reaches one.
780    /// The displacement is carried to the relocation's addend, and the four bytes it would have
781    /// gone in are left as nothing once the relocation is written.
782    fn addr(
783        &self,
784        operands: &[Operand],
785        amode: Option<&Amode>,
786        opcode: &str,
787    ) -> Result<(Addr, Option<String>), Error> {
788        let Some(amode) = amode else {
789            return Ok((Addr::default(), None));
790        };
791        let base = match amode.base {
792            Some(at) => Some(self.phys(operands[usize::from(at)], opcode)?),
793            None => None,
794        };
795        let index = match amode.index {
796            Some(at) => Some(self.phys(operands[usize::from(at)], opcode)?),
797            None => None,
798        };
799        let symbol = amode.symbol.map(|symbol| self.names.resolve(symbol).to_owned());
800        // A block is reached the same way and leaves the same four bytes. What is different is who
801        // fills them in, which is this file rather than the linker, and that is the caller's to
802        // sort out: what it needs from here is that the address was written that way at all.
803        let names = symbol.is_some() || amode.block.is_some() || amode.table.is_some();
804        let rip = names && base.is_none() && index.is_none();
805        // Or a symbol's offset in its section added to a register, which is the linker's to fill
806        // in as well but is not counted from the instruction.
807        let linked = symbol.is_some() && amode.reach == Reach::Section;
808        let segment = amode.segment;
809        let addr = Addr { base, index, scale: amode.scale, disp: amode.disp, rip, segment, linked };
810        Ok((addr, if rip || linked { symbol } else { None }))
811    }
812
813    /// The real register one operand ended up in.
814    fn phys(&self, operand: Operand, opcode: &str) -> Result<PhysReg, Error> {
815        operand
816            .reg
817            .phys()
818            .ok_or_else(|| Error::Virtual { func: self.name.to_owned(), opcode: opcode.to_owned() })
819    }
820}
821
822/// Which relocation a read of a slot of the global offset table asks for, from the bytes of the
823/// instruction it is in.
824///
825/// The linker can turn a slot back into the address itself in only a few instructions: a `mov`
826/// from memory, `test`, the eight that do arithmetic from memory into a register, and a `call` or
827/// `jmp` through memory, none of them behind a `0x66`. gas asks for the relocation that allows it
828/// in those and the plain one everywhere else, and says whether there is a REX prefix, which is
829/// what the linker needs to know to rewrite the instruction in place.
830pub(crate) fn slot(bytes: &[u8]) -> Reference {
831    let mut rest = bytes;
832    let mut rex = false;
833    while let [first, tail @ ..] = rest {
834        match first {
835            0x66 => return Reference::GotKept,
836            0x26 | 0x2E | 0x36 | 0x3E | 0x64 | 0x65 | 0x67 | 0xF0 | 0xF2 | 0xF3 => rest = tail,
837            0x40..=0x4F => {
838                rex = true;
839                rest = tail;
840            }
841            _ => break,
842        }
843    }
844    let rewritten = match rest {
845        [0x8B | 0x85, ..] => true,
846        [0xFF, modrm, ..] => matches!((modrm >> 3) & 7, 2 | 4),
847        [op, ..] => *op & !0x38 == 0x03,
848        [] => false,
849    };
850    match (rewritten, rex) {
851        (false, _) => Reference::GotKept,
852        (true, true) => Reference::Got,
853        (true, false) => Reference::GotBare,
854    }
855}
856
857#[cfg(test)]
858mod tests {
859    use super::*;
860
861    use rucc_base::Interner;
862    use rucc_mir::{BlockCall, Mem, Opcode, Reg, Table};
863    use rucc_object::{Binding, Visibility};
864    use rucc_target::x86_64::{GPR, RAX, RCX, RDX};
865    use rucc_target::{Arch, Env, Os, Triple};
866
867    /// A linux x86-64 target, which is the one every case here is written for.
868    fn target() -> TargetInfo {
869        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
870    }
871
872    /// One function of one block, with those instructions in it, assembled.
873    fn write(build: impl FnOnce(&mut Func, &mut Interner)) -> Text {
874        let mut names = Interner::new();
875        let mut func = Func::new(names.intern("f"));
876        build(&mut func, &mut names);
877        assemble(&[func], &names, &target(), true, false)
878            .expect("a function that was allocated")
879            .text
880    }
881
882    /// Those bytes, as the hexadecimal a manual writes them in.
883    fn hex(bytes: &[u8]) -> String {
884        bytes.iter().map(|byte| format!("{byte:02x}")).collect::<Vec<_>>().join(" ")
885    }
886
887    /// An addition of two registers, which is the smallest instruction with operands there is.
888    fn add(func: &mut Func, names: &mut Interner) {
889        let block = func.create_block();
890        let add = Opcode::new(names.intern("x64.add_rr_32"));
891        func.build(block, add)
892            .operand(Operand::write(Reg::physical(RAX), GPR))
893            .operand(Operand::read(Reg::physical(RAX), GPR))
894            .operand(Operand::read(Reg::physical(RCX), GPR))
895            .finish();
896    }
897
898    #[test]
899    fn an_instruction_is_the_bytes_the_target_says_it_is() {
900        let text = write(add);
901        assert_eq!(hex(&text.bytes), "01 c8");
902        let f = Extent {
903            name: "f".to_owned(),
904            start: 0,
905            len: 2,
906            align: FUNC_ALIGN,
907            binding: Binding::Global,
908            visibility: Visibility::Default,
909            patch: None,
910            landings: Vec::new(),
911        };
912        assert_eq!(text.funcs, [f]);
913        assert!(text.relocs.is_empty());
914    }
915
916    #[test]
917    fn an_opcode_the_machine_has_no_single_instruction_for_is_all_the_ones_it_has() {
918        let text = write(|func, names| {
919            let block = func.create_block();
920            let cmp = Opcode::new(names.intern("x64.cmp_set_l_64"));
921            func.build(block, cmp)
922                .operand(Operand::write(Reg::physical(RAX), GPR))
923                .operand(Operand::read(Reg::physical(RCX), GPR))
924                .operand(Operand::read(Reg::physical(RDX), GPR))
925                .finish();
926        });
927        // The comparison at the width it was asked for and then the set, which is the same two
928        // instructions the assembly path writes and is why one description rather than two.
929        assert_eq!(hex(&text.bytes), "48 39 d1 0f 9c c0");
930    }
931
932    #[test]
933    fn an_opcode_that_is_not_an_instruction_is_no_bytes_at_all() {
934        let text = write(|func, names| {
935            let block = func.create_block();
936            let ret = Opcode::new(names.intern("x64.ret_val_32"));
937            func.build(block, ret).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
938        });
939        assert!(text.bytes.is_empty(), "{:?}", text.bytes);
940    }
941
942    #[test]
943    fn an_alignment_is_the_bytes_between_where_it_is_and_the_boundary_it_asks_for() {
944        let text = write(|func, names| {
945            let block = func.create_block();
946            let add = Opcode::new(names.intern("x64.add_rr_32"));
947            let align = Opcode::new(names.intern("x64.align"));
948            let two = |func: &mut Func| {
949                func.build(block, add)
950                    .operand(Operand::write(Reg::physical(RAX), GPR))
951                    .operand(Operand::read(Reg::physical(RAX), GPR))
952                    .operand(Operand::read(Reg::physical(RCX), GPR))
953                    .finish();
954            };
955            two(func);
956            func.build(block, align).imm(8).finish();
957            two(func);
958        });
959        // Two bytes of addition, six of nothing, two more of addition. The padding is the one byte
960        // instruction that does nothing rather than a run of zeroes, because the processor may walk
961        // through it to get to what comes after, which is the whole reason a program asks.
962        assert_eq!(hex(&text.bytes), "01 c8 90 90 90 90 90 90 01 c8");
963        // The section has to be told as well. A function aligned to eight inside a section aligned
964        // to one is aligned to eight in its own reckoning and to nothing at all in the program's.
965        assert!(text.align >= 8, "{}", text.align);
966    }
967
968    /// The bytes a template wrote out itself, which go down as they are.
969    ///
970    /// `xgetbv` written as its three bytes, which is how every program that has one writes it,
971    /// between two instructions so that what is checked is that the bytes land where the program
972    /// put them and not just that they land.
973    #[test]
974    fn a_byte_out_of_a_template_is_that_byte_and_nothing_around_it() {
975        let text = write(|func, names| {
976            let block = func.create_block();
977            let add = Opcode::new(names.intern("x64.add_rr_32"));
978            let byte = Opcode::new(names.intern("x64.byte"));
979            let two = |func: &mut Func| {
980                func.build(block, add)
981                    .operand(Operand::write(Reg::physical(RAX), GPR))
982                    .operand(Operand::read(Reg::physical(RAX), GPR))
983                    .operand(Operand::read(Reg::physical(RCX), GPR))
984                    .finish();
985            };
986            two(func);
987            let bytes = x86_64::packed(&[0x0f, 0x01, 0xd0]).expect("three bytes fit");
988            func.build(block, byte).imm(bytes).finish();
989            two(func);
990        });
991        assert_eq!(hex(&text.bytes), "01 c8 0f 01 d0 01 c8");
992    }
993
994    #[test]
995    fn a_jump_inside_a_function_is_filled_in_rather_than_left_to_the_linker() {
996        let mut names = Interner::new();
997        let mut func = Func::new(names.intern("f"));
998        let first = func.create_block();
999        let second = func.create_block();
1000        let add = Opcode::new(names.intern("x64.add_rr_32"));
1001        func.build(first, add)
1002            .operand(Operand::write(Reg::physical(RAX), GPR))
1003            .operand(Operand::read(Reg::physical(RAX), GPR))
1004            .operand(Operand::read(Reg::physical(RCX), GPR))
1005            .finish();
1006        let jmp = Opcode::new(names.intern("x64.jmp"));
1007        func.build(second, jmp).finish();
1008        func.succs_mut(second).push(BlockCall::to(first));
1009
1010        let text = assemble(&[func], &names, &target(), true, false).expect("two blocks").text;
1011        // Two bytes of addition, then a jump back over itself and over them, which is seven bytes
1012        // backwards because a jump counts from where it ends.
1013        assert_eq!(hex(&text.bytes), "01 c8 e9 f9 ff ff ff");
1014        assert!(text.relocs.is_empty(), "a jump inside a function is not the linker's business");
1015    }
1016
1017    #[test]
1018    fn the_address_of_a_label_is_filled_in_here_as_well() {
1019        let mut names = Interner::new();
1020        let mut func = Func::new(names.intern("f"));
1021        let first = func.create_block();
1022        let second = func.create_block();
1023        let lea = Opcode::new(names.intern("x64.lea_64"));
1024        func.build(first, lea)
1025            .operand(Operand::write(Reg::physical(RAX), GPR))
1026            .mem(Mem::block(second))
1027            .finish();
1028        let jmp = Opcode::new(names.intern("x64.jmp_reg"));
1029        func.build(first, jmp).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
1030        func.succs_mut(first).push(BlockCall::to(second));
1031        func.build(second, Opcode::new(names.intern("x64.ret"))).finish();
1032
1033        let text = assemble(&[func], &names, &target(), true, false).expect("two blocks").text;
1034        // Seven bytes of address, two of jump, and then the block. The distance is two, because
1035        // the four bytes count from the end of the instruction that holds them and the jump is
1036        // what is in between.
1037        assert_eq!(hex(&text.bytes), "48 8d 05 02 00 00 00 ff e0 c3");
1038        assert!(text.relocs.is_empty(), "a label of this function is not the linker's business");
1039    }
1040
1041    /// A function that jumps through a table of three cells to one of two returns.
1042    fn switching(names: &mut Interner) -> Func {
1043        let mut func = Func::new(names.intern("f"));
1044        let head = func.create_block();
1045        let first = func.create_block();
1046        let second = func.create_block();
1047        let lea = Opcode::new(names.intern("x64.lea_64"));
1048        func.build(head, lea)
1049            .operand(Operand::write(Reg::physical(RAX), GPR))
1050            .mem(Mem::table(0))
1051            .finish();
1052        let jmp = Opcode::new(names.intern("x64.jmp_reg"));
1053        let jump = func.build(head, jmp).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
1054        func.succs_mut(head).push(BlockCall::to(first));
1055        func.succs_mut(head).push(BlockCall::to(second));
1056        func.build(first, Opcode::new(names.intern("x64.ret"))).finish();
1057        func.build(second, Opcode::new(names.intern("x64.ret"))).finish();
1058        func.tables.push(Table { jump, cells: vec![0, 1, 0] });
1059        func
1060    }
1061
1062    #[test]
1063    fn a_jump_table_on_elf_goes_to_the_writer_with_where_each_block_is() {
1064        let mut names = Interner::new();
1065        let func = switching(&mut names);
1066        let text = assemble(&[func], &names, &target(), true, false).expect("a table").text;
1067        // Seven bytes of address, two of jump and two returns, and nothing after them: the table
1068        // is not in the code. The address is the linker's to fill in, counted from the end of
1069        // the instruction, which is four bytes past the hole.
1070        assert_eq!(hex(&text.bytes), "48 8d 05 00 00 00 00 ff e0 c3 c3");
1071        assert_eq!(
1072            text.relocs,
1073            [Reloc {
1074                at: 3,
1075                symbol: ".Lf_j0".to_owned(),
1076                kind: Reference::Data,
1077                addend: -4,
1078                after: 0
1079            }]
1080        );
1081        // The two returns are nine and ten bytes into the function.
1082        let table =
1083            rucc_object::Table { name: ".Lf_j0".to_owned(), func: 0, cells: vec![9, 10, 9] };
1084        assert_eq!(text.tables, [table]);
1085    }
1086
1087    #[test]
1088    fn a_jump_table_on_windows_is_written_after_the_code_as_distances_from_itself() {
1089        let mut names = Interner::new();
1090        let func = switching(&mut names);
1091        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Windows, Env::Gnu));
1092        let text = assemble(&[func], &names, &target, true, false).expect("a table").text;
1093        // Seven bytes of address, two of jump and two returns end at eleven, one byte that does
1094        // nothing brings the table to twelve, and each cell is how far back its block is from
1095        // there. The address counts from the end of its own instruction, so it is five.
1096        assert_eq!(
1097            hex(&text.bytes),
1098            "48 8d 05 05 00 00 00 ff e0 c3 c3 90 fd ff ff ff fe ff ff ff fd ff ff ff"
1099        );
1100        assert!(text.relocs.is_empty(), "a table of this function is not the linker's business");
1101        assert!(text.tables.is_empty(), "{:?}", text.tables);
1102    }
1103
1104    #[test]
1105    fn a_call_leaves_the_linker_the_name_of_what_it_calls() {
1106        let mut names = Interner::new();
1107        let mut func = Func::new(names.intern("f"));
1108        let block = func.create_block();
1109        let call = Opcode::new(names.intern("x64.call"));
1110        let callee = names.intern("puts");
1111        func.build(block, call).symbol(callee).finish();
1112
1113        let text = assemble(&[func], &names, &target(), true, false).expect("a call").text;
1114        assert_eq!(hex(&text.bytes), "e8 00 00 00 00");
1115        assert_eq!(
1116            text.relocs,
1117            [Reloc {
1118                at: 1,
1119                symbol: "puts".to_owned(),
1120                kind: Reference::Call,
1121                addend: -4,
1122                after: 0
1123            }]
1124        );
1125    }
1126
1127    #[test]
1128    fn a_global_is_a_relocation_counted_from_the_end_of_the_instruction() {
1129        let mut names = Interner::new();
1130        let mut func = Func::new(names.intern("f"));
1131        let block = func.create_block();
1132        let load = Opcode::new(names.intern("x64.mov_rm_64"));
1133        let global = names.intern("counter");
1134        func.build(block, load)
1135            .operand(Operand::write(Reg::physical(RAX), GPR))
1136            .mem(Mem::of(global).plus(8))
1137            .finish();
1138
1139        let text =
1140            assemble(&[func], &names, &target(), true, false).expect("a load of a global").text;
1141        // The four bytes are nothing, as gas leaves them, because tcc's linker adds to what is
1142        // there and would count the eight twice.
1143        assert_eq!(hex(&text.bytes), "48 8b 05 00 00 00 00");
1144        // Four bytes back to where the instruction ends, and then the eight the address already
1145        // meant. A relocation counts from where its own bytes start and an instruction counts
1146        // from where it ends, and the addend is what makes up the difference.
1147        assert_eq!(
1148            text.relocs,
1149            [Reloc {
1150                at: 3,
1151                symbol: "counter".to_owned(),
1152                kind: Reference::Data,
1153                addend: 4,
1154                after: 0
1155            }]
1156        );
1157    }
1158
1159    /// The room a patcher was promised, on both sides of the symbol.
1160    ///
1161    /// What holds the two halves to the same byte. The half in front of the label is written as a
1162    /// byte here and the half after it is encoded from the opcode like any other instruction, so
1163    /// this is what would notice if the machine ever encoded one of them as something else.
1164    #[test]
1165    fn an_entry_promised_to_a_patcher_is_bytes_that_do_nothing_on_both_sides_of_the_symbol() {
1166        let mut names = Interner::new();
1167        let mut func = Func::new(names.intern("f"));
1168        let block = func.create_block();
1169        let pad = Opcode::new(names.intern("x64.nop"));
1170        let first = func.build(block, pad).finish();
1171        func.build(block, pad).finish();
1172        add(&mut func, &mut names);
1173        func.patch = Some(rucc_mir::Patch { before: 3, pad, after: Some(first) });
1174
1175        let text = assemble(&[func], &names, &target(), true, false)
1176            .expect("a function with room in it")
1177            .text;
1178        assert_eq!(hex(&text.bytes), "90 90 90 90 90 01 c8");
1179        let [f] = &text.funcs[..] else { panic!("one function") };
1180        // The symbol is after the room in front of the label and its size counts none of it, which
1181        // is what makes a backtrace through the function name the function rather than the room.
1182        assert_eq!(f.start, 3);
1183        assert_eq!(f.len, 4);
1184        // And the record points at the front of the whole thing, which here is the front of the
1185        // function's bytes because there is room in front of the label.
1186        assert_eq!(f.patch, Some(Patch { at: 0, before: 3 }));
1187    }
1188
1189    /// The same when the room is all after the label, which is what one number asks for.
1190    #[test]
1191    fn room_that_is_all_after_the_label_is_recorded_where_it_really_starts() {
1192        let mut names = Interner::new();
1193        let mut func = Func::new(names.intern("f"));
1194        let block = func.create_block();
1195        // A landing pad in front of it, which is the one thing that goes between the label and the
1196        // room and is why the record is not just the top of the function.
1197        let landing = Opcode::new(names.intern("x64.endbr64"));
1198        func.build(block, landing).finish();
1199        let pad = Opcode::new(names.intern("x64.nop"));
1200        let first = func.build(block, pad).finish();
1201        func.build(block, pad).finish();
1202        add(&mut func, &mut names);
1203        func.patch = Some(rucc_mir::Patch { before: 0, pad, after: Some(first) });
1204
1205        let text = assemble(&[func], &names, &target(), true, false)
1206            .expect("a function with room in it")
1207            .text;
1208        assert_eq!(hex(&text.bytes), "f3 0f 1e fa 90 90 01 c8");
1209        let [f] = &text.funcs[..] else { panic!("one function") };
1210        assert_eq!(f.start, 0);
1211        assert_eq!(f.patch, Some(Patch { at: 4, before: 0 }));
1212    }
1213
1214    #[test]
1215    fn a_global_read_out_of_the_offset_table_asks_for_the_relocation_that_names_the_slot() {
1216        let mut names = Interner::new();
1217        let mut func = Func::new(names.intern("f"));
1218        let block = func.create_block();
1219        let load = Opcode::new(names.intern("x64.mov_rm_64"));
1220        let away = names.intern("away");
1221        func.build(block, load)
1222            .operand(Operand::write(Reg::physical(RAX), GPR))
1223            .mem(Mem::got(away))
1224            .finish();
1225
1226        let text = assemble(&[func], &names, &target(), true, false)
1227            .expect("a load through the offset table")
1228            .text;
1229        // A `mov` with a REX prefix, which the relocation requires by name: the linker is allowed
1230        // to turn it back into a `lea`, and it can only do that when it knows what it is looking
1231        // at down to the prefix.
1232        assert_eq!(hex(&text.bytes), "48 8b 05 00 00 00 00");
1233        assert_eq!(
1234            text.relocs,
1235            [Reloc {
1236                at: 3,
1237                symbol: "away".to_owned(),
1238                kind: Reference::Got,
1239                addend: -4,
1240                after: 0
1241            }]
1242        );
1243    }
1244
1245    #[test]
1246    fn an_address_that_names_a_register_is_not_a_relocation() {
1247        let text = write(|func, names| {
1248            let block = func.create_block();
1249            let lea = Opcode::new(names.intern("x64.lea_64"));
1250            func.build(block, lea)
1251                .operand(Operand::write(Reg::physical(RAX), GPR))
1252                .mem(
1253                    Mem::at(Operand::read(Reg::physical(RCX), GPR))
1254                        .indexed(Operand::read(Reg::physical(RDX), GPR), 4)
1255                        .plus(-16),
1256                )
1257                .finish();
1258        });
1259        assert_eq!(hex(&text.bytes), "48 8d 44 91 f0");
1260        assert!(text.relocs.is_empty());
1261    }
1262
1263    #[test]
1264    fn every_function_starts_on_a_boundary_and_the_space_in_front_of_one_does_nothing() {
1265        let mut names = Interner::new();
1266        let mut first = Func::new(names.intern("f"));
1267        add(&mut first, &mut names);
1268        let mut second = Func::new(names.intern("g"));
1269        add(&mut second, &mut names);
1270
1271        let text =
1272            assemble(&[first, second], &names, &target(), true, false).expect("two functions").text;
1273        assert_eq!(text.funcs[1].start, 16);
1274        assert_eq!(text.bytes.len(), 18);
1275        assert!(text.bytes[2..16].iter().all(|byte| *byte == NOP), "{:?}", text.bytes);
1276    }
1277
1278    #[test]
1279    fn a_function_that_was_never_allocated_is_refused_rather_than_encoded_wrongly() {
1280        let mut names = Interner::new();
1281        let mut func = Func::new(names.intern("f"));
1282        let block = func.create_block();
1283        let vreg = func.new_vreg(GPR);
1284        let neg = Opcode::new(names.intern("x64.neg_r_32"));
1285        func.build(block, neg).operand(Operand::write(vreg, GPR)).finish();
1286        let error =
1287            assemble(&[func], &names, &target(), true, false).expect_err("a virtual register");
1288        assert_eq!(
1289            error,
1290            Error::Virtual { func: "f".to_owned(), opcode: "x64.neg_r_32".to_owned() }
1291        );
1292    }
1293
1294    #[test]
1295    fn an_opcode_the_target_does_not_describe_is_refused() {
1296        let mut names = Interner::new();
1297        let mut func = Func::new(names.intern("f"));
1298        let block = func.create_block();
1299        let made_up = Opcode::new(names.intern("x64.frobnicate"));
1300        func.build(block, made_up).finish();
1301        let error =
1302            assemble(&[func], &names, &target(), true, false).expect_err("no such instruction");
1303        assert_eq!(
1304            error,
1305            Error::Opcode { func: "f".to_owned(), opcode: "x64.frobnicate".to_owned() }
1306        );
1307    }
1308
1309    #[test]
1310    fn a_build_that_asked_for_debug_information_is_told_where_each_instruction_began() {
1311        let mut names = Interner::new();
1312        let mut func = Func::new(names.intern("f"));
1313        let block = func.create_block();
1314        let add = Opcode::new(names.intern("x64.add_rr_32"));
1315        for at in 0..2u32 {
1316            func.build(block, add)
1317                .at(Span::new(at * 10, at * 10 + 3))
1318                .operand(Operand::write(Reg::physical(RAX), GPR))
1319                .operand(Operand::read(Reg::physical(RAX), GPR))
1320                .operand(Operand::read(Reg::physical(RCX), GPR))
1321                .finish();
1322        }
1323
1324        // And which instruction each row is for, which the line table has no use for and the
1325        // locations do, since a stretch a local is somewhere over is named by an instruction at
1326        // each end and this is where one gets an address.
1327        let line: Vec<Inst> = func.blocks().flat_map(|block| func.insts(block)).collect();
1328        let out = assemble(&[func], &names, &target(), true, true).expect("two instructions");
1329        assert_eq!(
1330            out.lines,
1331            vec![vec![
1332                Row { at: 0, span: Span::new(0, 3), inst: Some(line[0]) },
1333                Row { at: 2, span: Span::new(10, 13), inst: Some(line[1]) },
1334            ]]
1335        );
1336    }
1337
1338    #[test]
1339    fn a_function_that_knows_where_it_was_declared_says_so_over_its_prologue() {
1340        // The front of a function is instructions no expression in the source asked for, so
1341        // nothing there carries a span and the bytes would be covered by nothing. The declaration
1342        // is what gcc puts over them and it is what this puts over them too, as a row at zero in
1343        // front of everything the body produced.
1344        let mut names = Interner::new();
1345        let mut func = Func::new(names.intern("f"));
1346        func.declared = Span::new(100, 104);
1347        let block = func.create_block();
1348        let add = Opcode::new(names.intern("x64.add_rr_32"));
1349        // The first with no span, the way every instruction a prologue is made of has none, and
1350        // the second with one, the way an instruction the body asked for does.
1351        for span in [Span::DUMMY, Span::new(10, 13)] {
1352            func.build(block, add)
1353                .at(span)
1354                .operand(Operand::write(Reg::physical(RAX), GPR))
1355                .operand(Operand::read(Reg::physical(RAX), GPR))
1356                .operand(Operand::read(Reg::physical(RCX), GPR))
1357                .finish();
1358        }
1359
1360        // The row for the declaration is the one row here no instruction wrote, which is what
1361        // says the bytes it covers are the prologue's.
1362        let line: Vec<Inst> = func.blocks().flat_map(|block| func.insts(block)).collect();
1363        let out = assemble(&[func], &names, &target(), true, true).expect("two instructions");
1364        assert_eq!(
1365            out.lines,
1366            vec![vec![
1367                Row { at: 0, span: Span::new(100, 104), inst: None },
1368                Row { at: 0, span: Span::DUMMY, inst: Some(line[0]) },
1369                Row { at: 2, span: Span::new(10, 13), inst: Some(line[1]) },
1370            ]]
1371        );
1372    }
1373
1374    #[test]
1375    fn a_build_that_asked_for_none_carries_no_rows_at_all() {
1376        let mut names = Interner::new();
1377        let mut func = Func::new(names.intern("f"));
1378        add(&mut func, &mut names);
1379
1380        let out = assemble(&[func], &names, &target(), true, false).expect("one instruction");
1381        assert_eq!(out.lines, vec![Vec::new()]);
1382    }
1383
1384    #[test]
1385    fn a_machine_with_no_encoder_here_is_said_so_rather_than_encoded_as_x86_64() {
1386        let names = Interner::new();
1387        let aarch64 = TargetInfo::new(Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu));
1388        let error = assemble(&[], &names, &aarch64, true, false).expect_err("no encoder");
1389        assert!(matches!(error, Error::Machine { .. }), "{error:?}");
1390    }
1391
1392    /// A loop that would cross a line starts on the next one, the gap is instructions that do
1393    /// nothing, and a loop that fits where it falls is left there.
1394    #[test]
1395    fn the_head_of_a_loop_that_would_cross_a_line_starts_on_the_next_one() {
1396        let laid = |ahead: usize| {
1397            write(|func, names| {
1398                let first = func.create_block();
1399                let head = func.create_block();
1400                let add = Opcode::new(names.intern("x64.add_rr_32"));
1401                for block in std::iter::repeat_n(first, ahead).chain(std::iter::repeat_n(head, 15))
1402                {
1403                    func.build(block, add)
1404                        .operand(Operand::write(Reg::physical(RAX), GPR))
1405                        .operand(Operand::read(Reg::physical(RAX), GPR))
1406                        .operand(Operand::read(Reg::physical(RCX), GPR))
1407                        .finish();
1408                }
1409                func.build(head, Opcode::new(names.intern("x64.jmp"))).finish();
1410                func.succs_mut(head).push(BlockCall::to(head));
1411                func.heads = vec![head];
1412            })
1413        };
1414        // Fifteen adds and the five byte jump back are a loop of thirty five bytes. Twenty adds in
1415        // front put it at forty, which crosses at sixty four, so it moves there.
1416        let text = laid(20);
1417        assert_eq!(text.bytes.len(), 64 + 35);
1418        assert_eq!(hex(&text.bytes[64..66]), "01 c8");
1419        assert!(text.bytes[40..64].iter().all(|&byte| byte != 0x01), "only padding in the gap");
1420        assert_eq!(text.bytes[40], 0x66, "a long nop rather than single bytes");
1421        assert!(text.align >= 64, "{}", text.align);
1422        // Ten adds in front put it at twenty, and it ends at fifty five without crossing.
1423        let text = laid(10);
1424        assert_eq!(text.bytes.len(), 20 + 35);
1425        assert_eq!(hex(&text.bytes[20..22]), "01 c8");
1426    }
1427}