Skip to main content

llvm_codegen/
assembler.rs

1//! Integrated assembler (MC) stage.
2//!
3//! This module makes the direct machine-code emission path explicit:
4//! machine IR -> target encoder (`Emitter`) -> object sections/symbols ->
5//! object bytes. No textual assembly round-trip is required.
6
7use crate::emit::{emit_object, Emitter, ObjectFile};
8use crate::isel::MachineFunction;
9
10/// Summary metrics for one assembly invocation.
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub struct McAssemblyReport {
13    /// Public API for `section_count`.
14    pub section_count: usize,
15    /// Public API for `symbol_count`.
16    pub symbol_count: usize,
17    /// Public API for `reloc_count`.
18    pub reloc_count: usize,
19    /// Public API for `bytes`.
20    pub bytes: usize,
21}
22
23/// Result of integrated assembly.
24#[derive(Clone, Debug)]
25pub struct AssembledObject {
26    /// Public API for `object`.
27    pub object: ObjectFile,
28    /// Public API for `bytes`.
29    pub bytes: Vec<u8>,
30    /// Public API for `report`.
31    pub report: McAssemblyReport,
32}
33
34/// Pluggable assembler interface for machine-code/object emission.
35pub trait McAssembler {
36    fn assemble_object(&mut self, mf: &MachineFunction, emitter: &mut dyn Emitter) -> ObjectFile;
37
38    fn assemble(&mut self, mf: &MachineFunction, emitter: &mut dyn Emitter) -> AssembledObject {
39        let object = self.assemble_object(mf, emitter);
40        let bytes = object.to_bytes();
41        let reloc_count = object.sections.iter().map(|s| s.relocs.len()).sum();
42        let report = McAssemblyReport {
43            section_count: object.sections.len(),
44            symbol_count: object.symbols.len(),
45            reloc_count,
46            bytes: bytes.len(),
47        };
48        AssembledObject {
49            object,
50            bytes,
51            report,
52        }
53    }
54}
55
56/// Default integrated assembler: directly lowers machine IR to object bytes.
57#[derive(Clone, Copy, Debug, Default)]
58pub struct IntegratedAssembler;
59
60impl McAssembler for IntegratedAssembler {
61    fn assemble_object(&mut self, mf: &MachineFunction, emitter: &mut dyn Emitter) -> ObjectFile {
62        emit_object(mf, emitter)
63    }
64}
65
66/// Convenience wrapper that assembles machine IR directly into an object.
67pub fn assemble_object(mf: &MachineFunction, emitter: &mut dyn Emitter) -> ObjectFile {
68    IntegratedAssembler.assemble_object(mf, emitter)
69}
70
71/// Convenience wrapper that assembles machine IR directly into raw bytes.
72pub fn assemble_bytes(mf: &MachineFunction, emitter: &mut dyn Emitter) -> Vec<u8> {
73    IntegratedAssembler.assemble(mf, emitter).bytes
74}
75
76/// Convenience wrapper returning object, bytes, and assembly report.
77pub fn assemble_with_report(mf: &MachineFunction, emitter: &mut dyn Emitter) -> AssembledObject {
78    IntegratedAssembler.assemble(mf, emitter)
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::emit::{ObjectFormat, Section, Symbol};
85    use crate::isel::{MachineBlock, MachineFunction};
86
87    struct NopEmitter;
88    impl Emitter for NopEmitter {
89        fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
90            Section {
91                name: ".text".into(),
92                data: vec![0x90, 0xC3],
93                relocs: vec![],
94                debug_rows: vec![],
95            }
96        }
97        fn object_format(&self) -> ObjectFormat {
98            ObjectFormat::Elf
99        }
100    }
101
102    #[test]
103    fn integrated_assembler_produces_nonempty_bytes() {
104        let mut mf = MachineFunction::new("f".into());
105        mf.blocks.push(MachineBlock {
106            label: "entry".into(),
107            instrs: vec![],
108        });
109        let mut emitter = NopEmitter;
110        let assembled = assemble_with_report(&mf, &mut emitter);
111        assert!(!assembled.bytes.is_empty());
112        assert_eq!(
113            assembled.report.section_count,
114            assembled.object.sections.len()
115        );
116    }
117
118    #[test]
119    fn report_counts_sections_symbols_and_relocs() {
120        let object = ObjectFile {
121            format: ObjectFormat::Elf,
122            elf_machine: 62,
123            coff_machine: 0x8664,
124            sections: vec![Section {
125                name: ".text".into(),
126                data: vec![0xC3],
127                relocs: vec![],
128                debug_rows: vec![],
129            }],
130            symbols: vec![Symbol {
131                name: "f".into(),
132                section: 0,
133                offset: 0,
134                size: 1,
135                global: true,
136            }],
137        };
138        let bytes = object.to_bytes();
139        let report = McAssemblyReport {
140            section_count: object.sections.len(),
141            symbol_count: object.symbols.len(),
142            reloc_count: object.sections.iter().map(|s| s.relocs.len()).sum(),
143            bytes: bytes.len(),
144        };
145        assert_eq!(report.section_count, 1);
146        assert_eq!(report.symbol_count, 1);
147        assert_eq!(report.reloc_count, 0);
148        assert!(report.bytes > 0);
149    }
150}