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 rucc_base::Interner;
38use rucc_diag::Span;
39use rucc_mir::{Amode, Block, Func, Inst, Operand, Reach, defs};
40use rucc_target::x86_64::{self, Addr, Arg, RAX, Value, Width};
41use rucc_target::{PhysReg, TargetInfo};
42use rucc_tuple::Arch;
43
44use rucc_object::{Chunk, Extent, FUNC_ALIGN, Marker, Patch, Reference, Reloc, Text};
45
46use crate::Error;
47use crate::format::{binding, visibility};
48use crate::unwind::{self, Rows};
49
50/// The prefix every x86-64 opcode carries in the machine IR.
51const PREFIX: &str = "x64.";
52
53/// The one byte instruction that does nothing, which is what the space in front of a function is.
54///
55/// Also what the room a patcher was promised is made of. The two are the same byte and not the same
56/// thing: the padding is space nothing reaches, and the room is space something jumps into once it
57/// has been written over. See `assemble`.
58const NOP: u8 = 0x90;
59
60/// Where one machine instruction ended up, and where in the source it came from.
61///
62/// The span rather than a file and a line, because this layer has no source map and no business
63/// acquiring one. Turning a span into a place is the driver's, which is also where the paths a
64/// `-ffile-prefix-map` rewrites are still paths.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct Row {
67    /// How far into its own function the instruction begins.
68    pub at: usize,
69    /// What the machine IR said this instruction was for.
70    pub span: Span,
71    /// Which instruction of the machine function it is, or `None` for the row the prologue gets,
72    /// which is the one row here that no instruction wrote.
73    ///
74    /// The line table has no use for it and the locations do: a local the allocator kept in a
75    /// register is somewhere over a stretch the back end named by an instruction at each end,
76    /// because a machine instruction has no length until something encodes it, and this is where
77    /// it gets one. Carried on the row rather than as a second list because the two are the same
78    /// walk and a second list is a thing that can come to disagree with the first.
79    pub inst: Option<Inst>,
80}
81
82/// A text section and, when the build asked for it, where each instruction in it came from.
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct Assembled {
85    /// The instructions, and what the linker has to be told about them.
86    pub text: Text,
87    /// One list per function of [`Text::funcs`], in the same order, and empty throughout in a
88    /// build that asked for no debug information.
89    pub lines: Vec<Vec<Row>>,
90    /// The frame rules as `.debug_frame`, in a build that asked for debug information and for no
91    /// unwind table, where it is the only table a debugger has to find a frame base through. None
92    /// in every other build, and on a format that has no such section.
93    pub frames: Option<Chunk>,
94}
95
96/// Every function, as the bytes of a text section.
97///
98/// `unwind` is whether a function is described to an unwinder, which is
99/// `rucc_session::Options::unwinds` and is asked of the build rather than worked out here, so that
100/// this and the text writer cannot answer it differently for one function.
101///
102/// `lines` is whether to record where each instruction came from, which is
103/// `rucc_session::Options::debug_info` and is asked the same way and for the same reason. It is a
104/// question rather than something always answered because the rows are one per machine instruction
105/// and a build that is not writing debug information would carry them the length of the back end to
106/// throw them away.
107///
108/// # Errors
109///
110/// [`Error::Machine`] for an architecture nothing here encodes, and the rest for a function that
111/// should not have got this far. See [`Error`].
112///
113/// # Panics
114///
115/// Panics on a function that was promised room for a patcher and has none on either side of its
116/// own label, which is a prologue that recorded room it did not write.
117pub fn assemble(
118    funcs: &[Func],
119    names: &Interner,
120    target: &TargetInfo,
121    unwind: bool,
122    lines: bool,
123) -> Result<Assembled, Error> {
124    if target.tuple.arch() != Arch::X86_64 {
125        return Err(Error::Machine { triple: target.tuple.to_string() });
126    }
127    let mut text = Text::default();
128    let mut all = Vec::new();
129    // Where each function's frame rules landed, kept beside the extents rather than written into
130    // the section as they are found, because a record counts from the start of its function and the
131    // function's own length is not known until its last instruction has been encoded.
132    let mut rows = Vec::with_capacity(funcs.len());
133    for func in funcs {
134        // What this function asked for, which pads the space in front of it and, once every
135        // function has been through here, is what the whole section is aligned to. Both halves
136        // are needed: the offset inside the section is this padding and where the section itself
137        // lands is the alignment recorded on it. It goes on the extent as well, because under
138        // `-ffunction-sections` this function is a section of its own and the padding in front of
139        // it is gone, so this number is the only thing left saying what it wanted.
140        let align = func.align.unwrap_or(FUNC_ALIGN);
141        text.align = text.align.max(align);
142        let step = usize::try_from(align).unwrap_or(1).max(1);
143        while text.bytes.len() % step != 0 {
144            text.bytes.push(NOP);
145        }
146        // The half of the room a patcher was promised that is in front of the function's own
147        // label, laid down here because it is the one part of a finished function that is not in a
148        // block. What makes it the space in front of the function rather than the start of it is
149        // everything below: the symbol, the size and the record an unwinder reads all begin after
150        // it, which is what gcc does with the same flag and what a debugger showing a backtrace
151        // through a patched function needs.
152        //
153        // The byte is written rather than encoded because the room is counted in bytes and the
154        // instruction that fills it has no operands. `an_entry_promised_to_a_patcher_is_bytes_that
155        // _do_nothing_on_both_sides_of_the_symbol` is what holds it to the same byte the encoder
156        // writes for the half that is in a block.
157        let ahead = text.bytes.len();
158        if let Some(patch) = func.patch {
159            text.bytes.extend(std::iter::repeat_n(NOP, patch.before as usize));
160        }
161        let start = text.bytes.len();
162        let name = names.resolve(func.name).to_owned();
163        let mut assembler = Assembler {
164            names,
165            func,
166            name: &name,
167            text: &mut text,
168            blocks: Vec::new(),
169            jumps: Vec::new(),
170            rows: Vec::new(),
171            lines: Vec::new(),
172            wants: lines,
173            start,
174            room: None,
175        };
176        assembler.func()?;
177        let room = assembler.room;
178        rows.push(std::mem::take(&mut assembler.rows));
179        all.push(std::mem::take(&mut assembler.lines));
180        let len = text.bytes.len() - start;
181        // Where the record points is the front of the room, which is the half in front of the
182        // label in a function that has one and the first instruction of the other half otherwise.
183        // The two are not one offset because a landing pad can sit between the halves.
184        let patch = func.patch.map(|patch| {
185            let at = if patch.before > 0 {
186                ahead
187            } else {
188                room.expect("room that is neither in front of the label nor anywhere after it")
189            };
190            Patch { at, before: patch.before as usize }
191        });
192        text.funcs.push(Extent {
193            name,
194            start,
195            len,
196            align,
197            binding: binding(func.binding),
198            visibility: visibility(func.visibility),
199            patch,
200        });
201    }
202    // In whichever of the two shapes the target reads, which is what decides whether a prologue
203    // this cannot describe is a refusal or is nothing at all. See [`unwind::table`].
204    // Or, when there is to be no unwind table and there is to be debug information, the same rows
205    // where only a debugger looks. See [`unwind::debug_frame`].
206    let mut frames = None;
207    if let Some(conv) = target.call_regs {
208        if unwind {
209            text.unwind = unwind::table(&text.funcs, &rows, conv, target.object_format)?;
210        } else if lines {
211            frames = unwind::debug_frame(&text.funcs, &rows, conv, target.object_format);
212        }
213    }
214    Ok(Assembled { text, lines: all, frames })
215}
216
217/// A jump inside a function, waiting for the block it goes to to have a place.
218struct Jump {
219    /// Where the four bytes the distance goes in begin.
220    at: usize,
221    /// Where the instruction it belongs to ends, which is what the distance is counted from.
222    end: usize,
223    /// The place it goes to.
224    to: To,
225    /// What is added to the distance, which is nothing for a jump and is the displacement for an
226    /// address that names a block and has one.
227    disp: i64,
228}
229
230/// A place in this function that an instruction can name: a block, or one of its jump tables.
231#[derive(Clone, Copy)]
232enum To {
233    Block(Block),
234    Table(u32),
235}
236
237/// One function being written out.
238struct Assembler<'a> {
239    names: &'a Interner,
240    func: &'a Func,
241    name: &'a str,
242    text: &'a mut Text,
243    /// Where each block starts, indexed by the block's own number, or [`usize::MAX`] for one that
244    /// is not in the layout.
245    blocks: Vec<usize>,
246    jumps: Vec<Jump>,
247    /// The frame rules, each with how far into this function the instruction that changed them
248    /// ended.
249    rows: Rows,
250    /// Where each machine instruction began and what it was for, in the order they were written.
251    ///
252    /// Empty in a build that asked for no debug information, which is what `wants` says.
253    lines: Vec<Row>,
254    /// Whether to fill `lines` in at all.
255    wants: bool,
256    /// Where this function starts in the section, which is what those distances are counted from.
257    start: usize,
258    /// Where the room a patcher was promised after the label began, which is where the instruction
259    /// [`rucc_mir::Patch::after`] names was encoded.
260    ///
261    /// [`None`] in a function that was promised none and in one whose room is all in front of the
262    /// label, which is the same answer to two different questions and is why the caller decides
263    /// which of them it asked. See `assemble`.
264    room: Option<usize>,
265}
266
267impl Assembler<'_> {
268    /// The blocks, and then the jumps between them once every block has a place.
269    fn func(&mut self) -> Result<(), Error> {
270        self.blocks = vec![usize::MAX; self.func.block_count()];
271        // The prologue, first, because nothing in it has a span of its own. The pushes, the frame
272        // and the moves that put the arguments where the body expects them came from no expression
273        // in the source, so without this the front of every function is the one part of it no row
274        // covers, and a program counter in there gets no answer at all rather than a slightly
275        // early one. Where the function was declared is what gcc says over those bytes.
276        if self.wants && !self.func.declared.is_dummy() {
277            self.lines.push(Row { at: 0, span: self.func.declared, inst: None });
278        }
279        let end = self.func.cfi_end();
280        for block in self.func.blocks() {
281            self.blocks[block.index()] = self.text.bytes.len();
282            // And the name an image knows the block by, as a symbol at the same byte. The number
283            // the jumps above use is worked out here and stays here, because both ends of a jump
284            // are in this section. An image is in another one, so what it holds is a relocation
285            // and a relocation names a symbol, which is what this is.
286            if let Some(label) = self.func.block_name(block) {
287                let name = self.names.resolve(label).to_owned();
288                self.text.labels.push(Marker { name, at: self.text.bytes.len() });
289            }
290            for inst in self.func.insts(block) {
291                // Before it is encoded, because what is wanted is where it begins and after this
292                // it has already been written. A landing pad is in front of it in a function that
293                // has one, which is why the room is found this way rather than measured from the
294                // top of the function.
295                if self.func.patch.is_some_and(|patch| patch.after == Some(inst)) {
296                    self.room = Some(self.text.bytes.len());
297                }
298                // Where it begins rather than where it ends, which is the other way round from the
299                // frame rules below and for the same reason they are that way round: a debugger is
300                // asking what a program counter is in the middle of, and an unwinder is asking what
301                // the frame looked like at a return address.
302                if self.wants {
303                    let at = self.text.bytes.len() - self.start;
304                    self.lines.push(Row { at, span: self.func.span(inst), inst: Some(inst) });
305                }
306                self.inst(block, inst)?;
307                if Some(inst) == end {
308                    continue;
309                }
310                // Where the instruction ended, because a row takes effect after the instruction
311                // that changed the answer and an unwinder is looking up a return address, which is
312                // the byte after a call rather than the call itself.
313                let at = self.text.bytes.len() - self.start;
314                self.rows.extend(self.func.cfi_after(inst).map(|op| (at, op)));
315            }
316        }
317        let tables = self.tables()?;
318        for jump in std::mem::take(&mut self.jumps) {
319            let to = match jump.to {
320                To::Block(block) => self.blocks[block.index()],
321                To::Table(table) => tables[table as usize],
322            };
323            debug_assert_ne!(to, usize::MAX, "a jump to a block that was never laid out");
324            let distance = i64::try_from(to).expect("a section this size") + jump.disp
325                - i64::try_from(jump.end).expect("a section this size");
326            let distance = i32::try_from(distance)
327                .map_err(|_| Error::Distance { func: self.name.to_owned(), bytes: distance })?;
328            self.text.bytes[jump.at..jump.at + 4].copy_from_slice(&distance.to_le_bytes());
329        }
330        Ok(())
331    }
332
333    /// The jump tables, after the last instruction, giving back where each one starts.
334    ///
335    /// Inside the function rather than in a section of data, which is what gcc does on ELF as
336    /// well: every cell is a distance from the table to a block, both ends are in this section,
337    /// and so the whole table is filled in here and the linker is told nothing. It also keeps
338    /// the table inside the function's extent, which is what `-ffunction-sections` moves around
339    /// as one piece. The cells are four bytes each and start on a four byte boundary, reached by
340    /// the byte that does nothing, although nothing ever runs into it: the last instruction of a
341    /// function is a return or a jump.
342    fn tables(&mut self) -> Result<Vec<usize>, Error> {
343        let mut starts = Vec::with_capacity(self.func.tables.len());
344        if self.func.tables.is_empty() {
345            return Ok(starts);
346        }
347        while self.text.bytes.len() % 4 != 0 {
348            self.text.bytes.push(NOP);
349        }
350        for table in &self.func.tables {
351            let start = self.text.bytes.len();
352            starts.push(start);
353            let block = self.func.block_of(table.jump).expect("a table read by a jump in no block");
354            let succs = &self.func[block].succs;
355            for &cell in &table.cells {
356                let to = self.blocks[succs[cell as usize].block.index()];
357                debug_assert_ne!(to, usize::MAX, "a table naming a block that was never laid out");
358                let distance = i64::try_from(to).expect("a section this size")
359                    - i64::try_from(start).expect("a section this size");
360                let distance = i32::try_from(distance)
361                    .map_err(|_| Error::Distance { func: self.name.to_owned(), bytes: distance })?;
362                self.text.bytes.extend_from_slice(&distance.to_le_bytes());
363            }
364        }
365        Ok(starts)
366    }
367
368    /// One instruction of the machine IR, as however many instructions of the machine it is.
369    fn inst(&mut self, block: Block, inst: Inst) -> Result<(), Error> {
370        let data = self.func[inst];
371        let spelled = self.names.resolve(data.opcode.name());
372        let opcode = spelled.strip_prefix(PREFIX).unwrap_or(spelled);
373        // The one opcode that is not an instruction. Where the listing writes the assembler's own
374        // directive this has to do what the assembler would have done, which is pad up to the
375        // boundary with the byte that does nothing, since the gap is reached by falling into it.
376        //
377        // The section has to be told as well. The padding puts the next instruction at a multiple of
378        // the boundary counted from the front of the section, and what makes that an address the
379        // program sees is the section itself landing on one, so the boundary goes on the section's
380        // alignment the way a function's own does.
381        if opcode == x86_64::ALIGN {
382            let bytes = data.imm.map_or(0, |imm| self.func[imm].0);
383            let boundary = u32::try_from(bytes).ok().filter(|at| at.is_power_of_two());
384            let Some(boundary) = boundary else {
385                return Err(Error::Opcode {
386                    func: self.name.to_owned(),
387                    opcode: spelled.to_owned(),
388                });
389            };
390            self.text.align = self.text.align.max(boundary);
391            let step = boundary as usize;
392            while self.text.bytes.len() % step != 0 {
393                self.text.bytes.push(NOP);
394            }
395            return Ok(());
396        }
397        // The other one, which is the bytes a template wrote out as themselves. There is nothing to
398        // encode: the program already said what the processor is to be handed, so they go down as
399        // they are.
400        if opcode == x86_64::LITERAL {
401            let Some(imm) = data.imm else {
402                return Err(Error::Opcode {
403                    func: self.name.to_owned(),
404                    opcode: spelled.to_owned(),
405                });
406            };
407            let before = self.text.bytes.len();
408            self.text.bytes.extend(x86_64::unpacked(self.func[imm].0));
409            if self.text.bytes.len() == before {
410                return Err(Error::Opcode {
411                    func: self.name.to_owned(),
412                    opcode: spelled.to_owned(),
413                });
414            }
415            return Ok(());
416        }
417        // A template kept as text has no bytes until an assembler reads the text, and a unit with
418        // one in it goes to the assembler as a listing rather than coming here. See
419        // [`crate::kept`]. Refused rather than written as nothing, which is what its empty row in
420        // the table would give.
421        if opcode == x86_64::TEMPLATE {
422            return Err(Error::Opcode { func: self.name.to_owned(), opcode: spelled.to_owned() });
423        }
424        let Some(written) = x86_64::written(opcode) else {
425            return Err(Error::Opcode { func: self.name.to_owned(), opcode: spelled.to_owned() });
426        };
427        let operands = &self.func[data.operands];
428        for machine in written {
429            // What each argument turned out to be, and what the encoder has to be told about
430            // afterwards for the ones that name something it cannot see.
431            let mut values = Vec::with_capacity(machine.args.len());
432            let mut wanted = None;
433            // The other thing an address can name, which is a place in this same function and so is
434            // a distance nothing outside the file has to be told about.
435            let mut labelled = None;
436            for arg in machine.args {
437                values.push(match *arg {
438                    Arg::Reg(at, width) => {
439                        Value::Reg(self.phys(operands[usize::from(at)], spelled)?, width)
440                    }
441                    // The same thing in the other file, which the encoder has to be told apart
442                    // from the one above: which file a register is in is part of which instruction
443                    // it is, and the table it looks a row up in is what says so.
444                    Arg::Xmm(at) => Value::Xmm(self.phys(operands[usize::from(at)], spelled)?),
445                    // The two halves of one word. The encoder numbers a high byte as the low one
446                    // plus four, which is the whole of the difference between them in the bytes
447                    // and is also why only the first four registers have one.
448                    Arg::Low(at) => {
449                        Value::Reg(self.phys(operands[usize::from(at)], spelled)?, Width::Byte)
450                    }
451                    Arg::High(at) => Value::High(self.phys(operands[usize::from(at)], spelled)?),
452                    // The only register named outright on this machine is the high half of the
453                    // first one, which an eight bit remainder comes back in.
454                    Arg::Named(_) => Value::High(RAX),
455                    // A depth on the x87 stack, which carries nothing across because there is
456                    // nothing to carry: the depth is in the opcode byte the mnemonic picks, so
457                    // what the encoder needs from here is that an argument was there at all.
458                    Arg::Stack(_) => Value::Stack,
459                    Arg::Lit(lane) => Value::Imm(i64::from(lane)),
460                    // The first operand read, which is where a call puts the address it goes
461                    // through. Everything in front of it is a register the call writes.
462                    Arg::Through => {
463                        Value::Reg(self.phys(operands[defs(operands)], spelled)?, Width::Quad)
464                    }
465                    Arg::Imm => Value::Imm(data.imm.map_or(0, |imm| self.func[imm].0)),
466                    Arg::Mem => {
467                        let amode = data.mem.map(|mem| self.func[mem]);
468                        let (addr, symbol) = self.addr(operands, amode.as_ref(), spelled)?;
469                        if let Some(symbol) = symbol {
470                            // A mode that reads the global offset table names the slot rather than
471                            // the thing, and the four bytes are the same four bytes either way, so
472                            // which relocation it is is the whole of the difference here.
473                            let kind = match amode.map_or(Reach::Itself, |mem| mem.reach) {
474                                Reach::Itself => Reference::Data,
475                                Reach::Table => Reference::Got,
476                                Reach::Thread => Reference::Thread,
477                            };
478                            wanted = Some((symbol, kind, i64::from(addr.disp)));
479                        }
480                        if let Some(block) = amode.and_then(|mem| mem.block) {
481                            labelled = Some((To::Block(block), i64::from(addr.disp)));
482                        }
483                        if let Some(table) = amode.and_then(|mem| mem.table) {
484                            labelled = Some((To::Table(table), i64::from(addr.disp)));
485                        }
486                        Value::Mem(addr)
487                    }
488                    Arg::Symbol => {
489                        let symbol =
490                            data.symbol.map(|symbol| self.names.resolve(symbol).to_owned());
491                        if let Some(symbol) = symbol {
492                            wanted = Some((symbol, Reference::Call, 0));
493                        }
494                        Value::Dest
495                    }
496                    // Where a conditional jump goes is the first arm, because the block layout
497                    // guarantees the second is the block laid out next and is fallen into.
498                    Arg::Label => Value::Dest,
499                });
500            }
501
502            let holes =
503                x86_64::encode(machine.mnemonic, &values, &mut self.text.bytes).map_err(|why| {
504                    Error::Encode {
505                        func: self.name.to_owned(),
506                        opcode: spelled.to_owned(),
507                        why: why.to_string(),
508                    }
509                })?;
510            let end = self.text.bytes.len();
511
512            // A hole is either something outside the file, which is a relocation, or a block of
513            // this function, which is patched once every block has a place.
514            if let Some((symbol, kind, disp)) = wanted {
515                let at = match kind {
516                    Reference::Call => holes.dest,
517                    Reference::Data | Reference::Got | Reference::Thread => holes.rip,
518                    // An address written into an image rather than reached by an instruction, and
519                    // how far something is from the front of one, which is what a table of data
520                    // holds. Nothing above produces either, because every reference an instruction
521                    // makes is a distance from where the instruction ends.
522                    Reference::Address { .. } | Reference::Image | Reference::Away => {
523                        unreachable!("an instruction wanting an address")
524                    }
525                };
526                let at = at.expect("an instruction naming a symbol leaves room for the distance");
527                let addend = disp - i64::try_from(end - at).expect("an instruction this long");
528                // How many bytes of the instruction come after the four the linker writes over,
529                // which is what is left of the distance from the hole to the end of it. Already in
530                // the addend and written down again because COFF wants the two apart, and there is
531                // nowhere else it can be worked out: by the time a writer sees the relocation the
532                // instruction it is in is bytes like any others.
533                let after = u8::try_from(end - at - 4).expect("an instruction this long");
534                self.text.relocs.push(Reloc { at, symbol, kind, addend, after });
535                // The addend is the whole of it, so the four bytes are left as nothing, which is
536                // what gas leaves. tcc's linker adds to what is there rather than writing over it,
537                // and a `mov cstr_buf+8(%rip)` with the eight in both places read eight bytes
538                // past the member it wanted.
539                self.text.bytes[at..at + 4].fill(0);
540            } else if let Some((to, disp)) = labelled {
541                // The address of a label, which is the four bytes an address counted from the
542                // instruction pointer leaves and is patched where a jump is patched rather than
543                // written out as a relocation, since both ends of it are in this function.
544                let at = holes.rip.expect("an address naming a label leaves room for the distance");
545                self.jumps.push(Jump { at, end, to, disp });
546            } else if let Some(at) = holes.dest {
547                match self.func[block].succs.first() {
548                    Some(call) => {
549                        self.jumps.push(Jump { at, end, to: To::Block(call.block), disp: 0 });
550                    }
551                    None => debug_assert!(false, "a jump out of a block with no arms"),
552                }
553            }
554        }
555        Ok(())
556    }
557
558    /// One address, with the operands it names resolved and the symbol it names handed back.
559    ///
560    /// A symbol with no base and no index is reached from the instruction pointer, which is how a
561    /// global is reached in position independent code and the only way this compiler reaches one.
562    /// The displacement is carried to the relocation's addend, and the four bytes it would have
563    /// gone in are left as nothing once the relocation is written.
564    fn addr(
565        &self,
566        operands: &[Operand],
567        amode: Option<&Amode>,
568        opcode: &str,
569    ) -> Result<(Addr, Option<String>), Error> {
570        let Some(amode) = amode else {
571            return Ok((Addr::default(), None));
572        };
573        let base = match amode.base {
574            Some(at) => Some(self.phys(operands[usize::from(at)], opcode)?),
575            None => None,
576        };
577        let index = match amode.index {
578            Some(at) => Some(self.phys(operands[usize::from(at)], opcode)?),
579            None => None,
580        };
581        let symbol = amode.symbol.map(|symbol| self.names.resolve(symbol).to_owned());
582        // A block is reached the same way and leaves the same four bytes. What is different is who
583        // fills them in, which is this file rather than the linker, and that is the caller's to
584        // sort out: what it needs from here is that the address was written that way at all.
585        let names = symbol.is_some() || amode.block.is_some() || amode.table.is_some();
586        let rip = names && base.is_none() && index.is_none();
587        let addr =
588            Addr { base, index, scale: amode.scale, disp: amode.disp, rip, segment: amode.segment };
589        Ok((addr, if rip { symbol } else { None }))
590    }
591
592    /// The real register one operand ended up in.
593    fn phys(&self, operand: Operand, opcode: &str) -> Result<PhysReg, Error> {
594        operand
595            .reg
596            .phys()
597            .ok_or_else(|| Error::Virtual { func: self.name.to_owned(), opcode: opcode.to_owned() })
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use super::*;
604
605    use rucc_base::Interner;
606    use rucc_mir::{BlockCall, Mem, Opcode, Reg, Table};
607    use rucc_object::{Binding, Visibility};
608    use rucc_target::x86_64::{GPR, RAX, RCX, RDX};
609    use rucc_target::{Arch, Env, Os, Triple};
610
611    /// A linux x86-64 target, which is the one every case here is written for.
612    fn target() -> TargetInfo {
613        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
614    }
615
616    /// One function of one block, with those instructions in it, assembled.
617    fn write(build: impl FnOnce(&mut Func, &mut Interner)) -> Text {
618        let mut names = Interner::new();
619        let mut func = Func::new(names.intern("f"));
620        build(&mut func, &mut names);
621        assemble(&[func], &names, &target(), true, false)
622            .expect("a function that was allocated")
623            .text
624    }
625
626    /// Those bytes, as the hexadecimal a manual writes them in.
627    fn hex(bytes: &[u8]) -> String {
628        bytes.iter().map(|byte| format!("{byte:02x}")).collect::<Vec<_>>().join(" ")
629    }
630
631    /// An addition of two registers, which is the smallest instruction with operands there is.
632    fn add(func: &mut Func, names: &mut Interner) {
633        let block = func.create_block();
634        let add = Opcode::new(names.intern("x64.add_rr_32"));
635        func.build(block, add)
636            .operand(Operand::write(Reg::physical(RAX), GPR))
637            .operand(Operand::read(Reg::physical(RAX), GPR))
638            .operand(Operand::read(Reg::physical(RCX), GPR))
639            .finish();
640    }
641
642    #[test]
643    fn an_instruction_is_the_bytes_the_target_says_it_is() {
644        let text = write(add);
645        assert_eq!(hex(&text.bytes), "01 c8");
646        let f = Extent {
647            name: "f".to_owned(),
648            start: 0,
649            len: 2,
650            align: FUNC_ALIGN,
651            binding: Binding::Global,
652            visibility: Visibility::Default,
653            patch: None,
654        };
655        assert_eq!(text.funcs, [f]);
656        assert!(text.relocs.is_empty());
657    }
658
659    #[test]
660    fn an_opcode_the_machine_has_no_single_instruction_for_is_all_the_ones_it_has() {
661        let text = write(|func, names| {
662            let block = func.create_block();
663            let cmp = Opcode::new(names.intern("x64.cmp_set_l_64"));
664            func.build(block, cmp)
665                .operand(Operand::write(Reg::physical(RAX), GPR))
666                .operand(Operand::read(Reg::physical(RCX), GPR))
667                .operand(Operand::read(Reg::physical(RDX), GPR))
668                .finish();
669        });
670        // The comparison at the width it was asked for and then the set, which is the same two
671        // instructions the assembly path writes and is why one description rather than two.
672        assert_eq!(hex(&text.bytes), "48 39 d1 0f 9c c0");
673    }
674
675    #[test]
676    fn an_opcode_that_is_not_an_instruction_is_no_bytes_at_all() {
677        let text = write(|func, names| {
678            let block = func.create_block();
679            let ret = Opcode::new(names.intern("x64.ret_val_32"));
680            func.build(block, ret).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
681        });
682        assert!(text.bytes.is_empty(), "{:?}", text.bytes);
683    }
684
685    #[test]
686    fn an_alignment_is_the_bytes_between_where_it_is_and_the_boundary_it_asks_for() {
687        let text = write(|func, names| {
688            let block = func.create_block();
689            let add = Opcode::new(names.intern("x64.add_rr_32"));
690            let align = Opcode::new(names.intern("x64.align"));
691            let two = |func: &mut Func| {
692                func.build(block, add)
693                    .operand(Operand::write(Reg::physical(RAX), GPR))
694                    .operand(Operand::read(Reg::physical(RAX), GPR))
695                    .operand(Operand::read(Reg::physical(RCX), GPR))
696                    .finish();
697            };
698            two(func);
699            func.build(block, align).imm(8).finish();
700            two(func);
701        });
702        // Two bytes of addition, six of nothing, two more of addition. The padding is the one byte
703        // instruction that does nothing rather than a run of zeroes, because the processor may walk
704        // through it to get to what comes after, which is the whole reason a program asks.
705        assert_eq!(hex(&text.bytes), "01 c8 90 90 90 90 90 90 01 c8");
706        // The section has to be told as well. A function aligned to eight inside a section aligned
707        // to one is aligned to eight in its own reckoning and to nothing at all in the program's.
708        assert!(text.align >= 8, "{}", text.align);
709    }
710
711    /// The bytes a template wrote out itself, which go down as they are.
712    ///
713    /// `xgetbv` written as its three bytes, which is how every program that has one writes it,
714    /// between two instructions so that what is checked is that the bytes land where the program
715    /// put them and not just that they land.
716    #[test]
717    fn a_byte_out_of_a_template_is_that_byte_and_nothing_around_it() {
718        let text = write(|func, names| {
719            let block = func.create_block();
720            let add = Opcode::new(names.intern("x64.add_rr_32"));
721            let byte = Opcode::new(names.intern("x64.byte"));
722            let two = |func: &mut Func| {
723                func.build(block, add)
724                    .operand(Operand::write(Reg::physical(RAX), GPR))
725                    .operand(Operand::read(Reg::physical(RAX), GPR))
726                    .operand(Operand::read(Reg::physical(RCX), GPR))
727                    .finish();
728            };
729            two(func);
730            let bytes = x86_64::packed(&[0x0f, 0x01, 0xd0]).expect("three bytes fit");
731            func.build(block, byte).imm(bytes).finish();
732            two(func);
733        });
734        assert_eq!(hex(&text.bytes), "01 c8 0f 01 d0 01 c8");
735    }
736
737    #[test]
738    fn a_jump_inside_a_function_is_filled_in_rather_than_left_to_the_linker() {
739        let mut names = Interner::new();
740        let mut func = Func::new(names.intern("f"));
741        let first = func.create_block();
742        let second = func.create_block();
743        let add = Opcode::new(names.intern("x64.add_rr_32"));
744        func.build(first, add)
745            .operand(Operand::write(Reg::physical(RAX), GPR))
746            .operand(Operand::read(Reg::physical(RAX), GPR))
747            .operand(Operand::read(Reg::physical(RCX), GPR))
748            .finish();
749        let jmp = Opcode::new(names.intern("x64.jmp"));
750        func.build(second, jmp).finish();
751        func.succs_mut(second).push(BlockCall::to(first));
752
753        let text = assemble(&[func], &names, &target(), true, false).expect("two blocks").text;
754        // Two bytes of addition, then a jump back over itself and over them, which is seven bytes
755        // backwards because a jump counts from where it ends.
756        assert_eq!(hex(&text.bytes), "01 c8 e9 f9 ff ff ff");
757        assert!(text.relocs.is_empty(), "a jump inside a function is not the linker's business");
758    }
759
760    #[test]
761    fn the_address_of_a_label_is_filled_in_here_as_well() {
762        let mut names = Interner::new();
763        let mut func = Func::new(names.intern("f"));
764        let first = func.create_block();
765        let second = func.create_block();
766        let lea = Opcode::new(names.intern("x64.lea_64"));
767        func.build(first, lea)
768            .operand(Operand::write(Reg::physical(RAX), GPR))
769            .mem(Mem::block(second))
770            .finish();
771        let jmp = Opcode::new(names.intern("x64.jmp_reg"));
772        func.build(first, jmp).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
773        func.succs_mut(first).push(BlockCall::to(second));
774        func.build(second, Opcode::new(names.intern("x64.ret"))).finish();
775
776        let text = assemble(&[func], &names, &target(), true, false).expect("two blocks").text;
777        // Seven bytes of address, two of jump, and then the block. The distance is two, because
778        // the four bytes count from the end of the instruction that holds them and the jump is
779        // what is in between.
780        assert_eq!(hex(&text.bytes), "48 8d 05 02 00 00 00 ff e0 c3");
781        assert!(text.relocs.is_empty(), "a label of this function is not the linker's business");
782    }
783
784    #[test]
785    fn a_jump_table_is_written_after_the_code_as_distances_from_itself() {
786        let mut names = Interner::new();
787        let mut func = Func::new(names.intern("f"));
788        let head = func.create_block();
789        let first = func.create_block();
790        let second = func.create_block();
791        let lea = Opcode::new(names.intern("x64.lea_64"));
792        func.build(head, lea)
793            .operand(Operand::write(Reg::physical(RAX), GPR))
794            .mem(Mem::table(0))
795            .finish();
796        let jmp = Opcode::new(names.intern("x64.jmp_reg"));
797        let jump = func.build(head, jmp).operand(Operand::read(Reg::physical(RAX), GPR)).finish();
798        func.succs_mut(head).push(BlockCall::to(first));
799        func.succs_mut(head).push(BlockCall::to(second));
800        func.build(first, Opcode::new(names.intern("x64.ret"))).finish();
801        func.build(second, Opcode::new(names.intern("x64.ret"))).finish();
802        func.tables.push(Table { jump, cells: vec![0, 1, 0] });
803
804        let text = assemble(&[func], &names, &target(), true, false).expect("a table").text;
805        // Seven bytes of address, two of jump and two returns end at eleven, one byte that does
806        // nothing brings the table to twelve, and each cell is how far back its block is from
807        // there. The address counts from the end of its own instruction, so it is five.
808        assert_eq!(
809            hex(&text.bytes),
810            "48 8d 05 05 00 00 00 ff e0 c3 c3 90 fd ff ff ff fe ff ff ff fd ff ff ff"
811        );
812        assert!(text.relocs.is_empty(), "a table of this function is not the linker's business");
813    }
814
815    #[test]
816    fn a_call_leaves_the_linker_the_name_of_what_it_calls() {
817        let mut names = Interner::new();
818        let mut func = Func::new(names.intern("f"));
819        let block = func.create_block();
820        let call = Opcode::new(names.intern("x64.call"));
821        let callee = names.intern("puts");
822        func.build(block, call).symbol(callee).finish();
823
824        let text = assemble(&[func], &names, &target(), true, false).expect("a call").text;
825        assert_eq!(hex(&text.bytes), "e8 00 00 00 00");
826        assert_eq!(
827            text.relocs,
828            [Reloc {
829                at: 1,
830                symbol: "puts".to_owned(),
831                kind: Reference::Call,
832                addend: -4,
833                after: 0
834            }]
835        );
836    }
837
838    #[test]
839    fn a_global_is_a_relocation_counted_from_the_end_of_the_instruction() {
840        let mut names = Interner::new();
841        let mut func = Func::new(names.intern("f"));
842        let block = func.create_block();
843        let load = Opcode::new(names.intern("x64.mov_rm_64"));
844        let global = names.intern("counter");
845        func.build(block, load)
846            .operand(Operand::write(Reg::physical(RAX), GPR))
847            .mem(Mem::of(global).plus(8))
848            .finish();
849
850        let text =
851            assemble(&[func], &names, &target(), true, false).expect("a load of a global").text;
852        // The four bytes are nothing, as gas leaves them, because tcc's linker adds to what is
853        // there and would count the eight twice.
854        assert_eq!(hex(&text.bytes), "48 8b 05 00 00 00 00");
855        // Four bytes back to where the instruction ends, and then the eight the address already
856        // meant. A relocation counts from where its own bytes start and an instruction counts
857        // from where it ends, and the addend is what makes up the difference.
858        assert_eq!(
859            text.relocs,
860            [Reloc {
861                at: 3,
862                symbol: "counter".to_owned(),
863                kind: Reference::Data,
864                addend: 4,
865                after: 0
866            }]
867        );
868    }
869
870    /// The room a patcher was promised, on both sides of the symbol.
871    ///
872    /// What holds the two halves to the same byte. The half in front of the label is written as a
873    /// byte here and the half after it is encoded from the opcode like any other instruction, so
874    /// this is what would notice if the machine ever encoded one of them as something else.
875    #[test]
876    fn an_entry_promised_to_a_patcher_is_bytes_that_do_nothing_on_both_sides_of_the_symbol() {
877        let mut names = Interner::new();
878        let mut func = Func::new(names.intern("f"));
879        let block = func.create_block();
880        let pad = Opcode::new(names.intern("x64.nop"));
881        let first = func.build(block, pad).finish();
882        func.build(block, pad).finish();
883        add(&mut func, &mut names);
884        func.patch = Some(rucc_mir::Patch { before: 3, pad, after: Some(first) });
885
886        let text = assemble(&[func], &names, &target(), true, false)
887            .expect("a function with room in it")
888            .text;
889        assert_eq!(hex(&text.bytes), "90 90 90 90 90 01 c8");
890        let [f] = &text.funcs[..] else { panic!("one function") };
891        // The symbol is after the room in front of the label and its size counts none of it, which
892        // is what makes a backtrace through the function name the function rather than the room.
893        assert_eq!(f.start, 3);
894        assert_eq!(f.len, 4);
895        // And the record points at the front of the whole thing, which here is the front of the
896        // function's bytes because there is room in front of the label.
897        assert_eq!(f.patch, Some(Patch { at: 0, before: 3 }));
898    }
899
900    /// The same when the room is all after the label, which is what one number asks for.
901    #[test]
902    fn room_that_is_all_after_the_label_is_recorded_where_it_really_starts() {
903        let mut names = Interner::new();
904        let mut func = Func::new(names.intern("f"));
905        let block = func.create_block();
906        // A landing pad in front of it, which is the one thing that goes between the label and the
907        // room and is why the record is not just the top of the function.
908        let landing = Opcode::new(names.intern("x64.endbr64"));
909        func.build(block, landing).finish();
910        let pad = Opcode::new(names.intern("x64.nop"));
911        let first = func.build(block, pad).finish();
912        func.build(block, pad).finish();
913        add(&mut func, &mut names);
914        func.patch = Some(rucc_mir::Patch { before: 0, pad, after: Some(first) });
915
916        let text = assemble(&[func], &names, &target(), true, false)
917            .expect("a function with room in it")
918            .text;
919        assert_eq!(hex(&text.bytes), "f3 0f 1e fa 90 90 01 c8");
920        let [f] = &text.funcs[..] else { panic!("one function") };
921        assert_eq!(f.start, 0);
922        assert_eq!(f.patch, Some(Patch { at: 4, before: 0 }));
923    }
924
925    #[test]
926    fn a_global_read_out_of_the_offset_table_asks_for_the_relocation_that_names_the_slot() {
927        let mut names = Interner::new();
928        let mut func = Func::new(names.intern("f"));
929        let block = func.create_block();
930        let load = Opcode::new(names.intern("x64.mov_rm_64"));
931        let away = names.intern("away");
932        func.build(block, load)
933            .operand(Operand::write(Reg::physical(RAX), GPR))
934            .mem(Mem::got(away))
935            .finish();
936
937        let text = assemble(&[func], &names, &target(), true, false)
938            .expect("a load through the offset table")
939            .text;
940        // A `mov` with a REX prefix, which the relocation requires by name: the linker is allowed
941        // to turn it back into a `lea`, and it can only do that when it knows what it is looking
942        // at down to the prefix.
943        assert_eq!(hex(&text.bytes), "48 8b 05 00 00 00 00");
944        assert_eq!(
945            text.relocs,
946            [Reloc {
947                at: 3,
948                symbol: "away".to_owned(),
949                kind: Reference::Got,
950                addend: -4,
951                after: 0
952            }]
953        );
954    }
955
956    #[test]
957    fn an_address_that_names_a_register_is_not_a_relocation() {
958        let text = write(|func, names| {
959            let block = func.create_block();
960            let lea = Opcode::new(names.intern("x64.lea_64"));
961            func.build(block, lea)
962                .operand(Operand::write(Reg::physical(RAX), GPR))
963                .mem(
964                    Mem::at(Operand::read(Reg::physical(RCX), GPR))
965                        .indexed(Operand::read(Reg::physical(RDX), GPR), 4)
966                        .plus(-16),
967                )
968                .finish();
969        });
970        assert_eq!(hex(&text.bytes), "48 8d 44 91 f0");
971        assert!(text.relocs.is_empty());
972    }
973
974    #[test]
975    fn every_function_starts_on_a_boundary_and_the_space_in_front_of_one_does_nothing() {
976        let mut names = Interner::new();
977        let mut first = Func::new(names.intern("f"));
978        add(&mut first, &mut names);
979        let mut second = Func::new(names.intern("g"));
980        add(&mut second, &mut names);
981
982        let text =
983            assemble(&[first, second], &names, &target(), true, false).expect("two functions").text;
984        assert_eq!(text.funcs[1].start, 16);
985        assert_eq!(text.bytes.len(), 18);
986        assert!(text.bytes[2..16].iter().all(|byte| *byte == NOP), "{:?}", text.bytes);
987    }
988
989    #[test]
990    fn a_function_that_was_never_allocated_is_refused_rather_than_encoded_wrongly() {
991        let mut names = Interner::new();
992        let mut func = Func::new(names.intern("f"));
993        let block = func.create_block();
994        let vreg = func.new_vreg(GPR);
995        let neg = Opcode::new(names.intern("x64.neg_r_32"));
996        func.build(block, neg).operand(Operand::write(vreg, GPR)).finish();
997        let error =
998            assemble(&[func], &names, &target(), true, false).expect_err("a virtual register");
999        assert_eq!(
1000            error,
1001            Error::Virtual { func: "f".to_owned(), opcode: "x64.neg_r_32".to_owned() }
1002        );
1003    }
1004
1005    #[test]
1006    fn an_opcode_the_target_does_not_describe_is_refused() {
1007        let mut names = Interner::new();
1008        let mut func = Func::new(names.intern("f"));
1009        let block = func.create_block();
1010        let made_up = Opcode::new(names.intern("x64.frobnicate"));
1011        func.build(block, made_up).finish();
1012        let error =
1013            assemble(&[func], &names, &target(), true, false).expect_err("no such instruction");
1014        assert_eq!(
1015            error,
1016            Error::Opcode { func: "f".to_owned(), opcode: "x64.frobnicate".to_owned() }
1017        );
1018    }
1019
1020    #[test]
1021    fn a_build_that_asked_for_debug_information_is_told_where_each_instruction_began() {
1022        let mut names = Interner::new();
1023        let mut func = Func::new(names.intern("f"));
1024        let block = func.create_block();
1025        let add = Opcode::new(names.intern("x64.add_rr_32"));
1026        for at in 0..2u32 {
1027            func.build(block, add)
1028                .at(Span::new(at * 10, at * 10 + 3))
1029                .operand(Operand::write(Reg::physical(RAX), GPR))
1030                .operand(Operand::read(Reg::physical(RAX), GPR))
1031                .operand(Operand::read(Reg::physical(RCX), GPR))
1032                .finish();
1033        }
1034
1035        // And which instruction each row is for, which the line table has no use for and the
1036        // locations do, since a stretch a local is somewhere over is named by an instruction at
1037        // each end and this is where one gets an address.
1038        let line: Vec<Inst> = func.blocks().flat_map(|block| func.insts(block)).collect();
1039        let out = assemble(&[func], &names, &target(), true, true).expect("two instructions");
1040        assert_eq!(
1041            out.lines,
1042            vec![vec![
1043                Row { at: 0, span: Span::new(0, 3), inst: Some(line[0]) },
1044                Row { at: 2, span: Span::new(10, 13), inst: Some(line[1]) },
1045            ]]
1046        );
1047    }
1048
1049    #[test]
1050    fn a_function_that_knows_where_it_was_declared_says_so_over_its_prologue() {
1051        // The front of a function is instructions no expression in the source asked for, so
1052        // nothing there carries a span and the bytes would be covered by nothing. The declaration
1053        // is what gcc puts over them and it is what this puts over them too, as a row at zero in
1054        // front of everything the body produced.
1055        let mut names = Interner::new();
1056        let mut func = Func::new(names.intern("f"));
1057        func.declared = Span::new(100, 104);
1058        let block = func.create_block();
1059        let add = Opcode::new(names.intern("x64.add_rr_32"));
1060        // The first with no span, the way every instruction a prologue is made of has none, and
1061        // the second with one, the way an instruction the body asked for does.
1062        for span in [Span::DUMMY, Span::new(10, 13)] {
1063            func.build(block, add)
1064                .at(span)
1065                .operand(Operand::write(Reg::physical(RAX), GPR))
1066                .operand(Operand::read(Reg::physical(RAX), GPR))
1067                .operand(Operand::read(Reg::physical(RCX), GPR))
1068                .finish();
1069        }
1070
1071        // The row for the declaration is the one row here no instruction wrote, which is what
1072        // says the bytes it covers are the prologue's.
1073        let line: Vec<Inst> = func.blocks().flat_map(|block| func.insts(block)).collect();
1074        let out = assemble(&[func], &names, &target(), true, true).expect("two instructions");
1075        assert_eq!(
1076            out.lines,
1077            vec![vec![
1078                Row { at: 0, span: Span::new(100, 104), inst: None },
1079                Row { at: 0, span: Span::DUMMY, inst: Some(line[0]) },
1080                Row { at: 2, span: Span::new(10, 13), inst: Some(line[1]) },
1081            ]]
1082        );
1083    }
1084
1085    #[test]
1086    fn a_build_that_asked_for_none_carries_no_rows_at_all() {
1087        let mut names = Interner::new();
1088        let mut func = Func::new(names.intern("f"));
1089        add(&mut func, &mut names);
1090
1091        let out = assemble(&[func], &names, &target(), true, false).expect("one instruction");
1092        assert_eq!(out.lines, vec![Vec::new()]);
1093    }
1094
1095    #[test]
1096    fn a_machine_with_no_encoder_here_is_said_so_rather_than_encoded_as_x86_64() {
1097        let names = Interner::new();
1098        let aarch64 = TargetInfo::new(Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu));
1099        let error = assemble(&[], &names, &aarch64, true, false).expect_err("no encoder");
1100        assert!(matches!(error, Error::Machine { .. }), "{error:?}");
1101    }
1102}