midenc_codegen_masm/compiler/
mod.rs

1mod masm;
2mod mast;
3
4use midenc_hir::{
5    self as hir,
6    pass::{RewritePass, RewriteSet},
7};
8use midenc_session::{diagnostics::Report, Session};
9
10pub use self::{masm::MasmArtifact, mast::MastArtifact};
11use crate::{intrinsics, ConvertHirToMasm, Program};
12
13pub type CompilerResult<T> = Result<T, Report>;
14
15/// [MasmCompiler] is a compiler from Miden IR to MASM IR, an intermediate representation
16/// of Miden Assembly which is used within the Miden compiler framework for various purposes,
17/// and can be emitted directly to textual Miden Assembly.
18///
19/// The [MasmCompiler] is designed to compile a [midenc_hir::Program]
20///
21/// can be used to take a linked [midenc_hir::Program] and
22/// compile it to MASM IR, an intermediate representation of Miden Assembly
23/// used within the compiler.
24pub struct MasmCompiler<'a> {
25    session: &'a Session,
26    analyses: hir::pass::AnalysisManager,
27}
28impl<'a> MasmCompiler<'a> {
29    pub fn new(session: &'a Session) -> Self {
30        Self {
31            session,
32            analyses: hir::pass::AnalysisManager::new(),
33        }
34    }
35
36    /// Compile an [hir::Program] that has been linked and is ready to be compiled.
37    pub fn compile(&mut self, mut input: Box<hir::Program>) -> CompilerResult<MasmArtifact> {
38        use midenc_hir::pass::ConversionPass;
39
40        let mut rewrites = default_rewrites([], self.session);
41
42        let modules = input.modules_mut().take();
43        for mut module in modules.into_iter() {
44            rewrites.apply(&mut module, &mut self.analyses, self.session)?;
45            input.modules_mut().insert(module);
46        }
47
48        let mut convert_to_masm = ConvertHirToMasm::<hir::Program>::default();
49        let mut artifact = convert_to_masm.convert(input, &mut self.analyses, self.session)?;
50
51        // Ensure intrinsics modules are linked
52        artifact.insert(Box::new(
53            intrinsics::load("intrinsics::mem", &self.session.source_manager)
54                .expect("undefined intrinsics module"),
55        ));
56        artifact.insert(Box::new(
57            intrinsics::load("intrinsics::i32", &self.session.source_manager)
58                .expect("undefined intrinsics module"),
59        ));
60        artifact.insert(Box::new(
61            intrinsics::load("intrinsics::i64", &self.session.source_manager)
62                .expect("undefined intrinsics module"),
63        ));
64
65        Ok(artifact)
66    }
67
68    /// Compile a single [hir::Module] as a program.
69    ///
70    /// It is assumed that the given module has been validated, and that all necessary
71    /// rewrites have been applied. If one of these invariants is not upheld, compilation
72    /// may fail.
73    pub fn compile_module(&mut self, input: Box<hir::Module>) -> CompilerResult<Box<Program>> {
74        assert!(input.entrypoint().is_some(), "cannot compile a program without an entrypoint");
75
76        let program =
77            hir::ProgramBuilder::new(&self.session.diagnostics).with_module(input)?.link()?;
78
79        match self.compile(program)? {
80            MasmArtifact::Executable(program) => Ok(program),
81            _ => unreachable!("expected compiler to produce an executable, got a library"),
82        }
83    }
84
85    /// Compile a set of [hir::Module] as a program.
86    ///
87    /// It is assumed that the given modules have been validated, and that all necessary
88    /// rewrites have been applied. If one of these invariants is not upheld, compilation
89    /// may fail.
90    pub fn compile_modules<I: IntoIterator<Item = Box<hir::Module>>>(
91        &mut self,
92        input: I,
93    ) -> CompilerResult<Box<Program>> {
94        let mut builder = hir::ProgramBuilder::new(&self.session.diagnostics);
95        for module in input.into_iter() {
96            builder.add_module(module)?;
97        }
98
99        let program = builder.link()?;
100
101        assert!(program.has_entrypoint(), "cannot compile a program without an entrypoint");
102
103        match self.compile(program)? {
104            MasmArtifact::Executable(program) => Ok(program),
105            _ => unreachable!("expected compiler to produce an executable, got a library"),
106        }
107    }
108}
109
110pub fn default_rewrites<P>(registered: P, session: &Session) -> RewriteSet<hir::Module>
111where
112    P: IntoIterator<Item = Box<dyn RewritePass<Entity = hir::Module>>>,
113    <P as IntoIterator>::IntoIter: ExactSizeIterator,
114{
115    use midenc_hir::pass::ModuleRewritePassAdapter;
116
117    let registered = registered.into_iter();
118
119    // If no rewrites were explicitly enabled, and conversion to Miden Assembly is,
120    // then we must ensure that the basic transformation passes are applied.
121    //
122    // Otherwise, assume that the intent was to skip those rewrites and do not add them
123    let mut rewrites = RewriteSet::default();
124    if registered.len() == 0 {
125        if session.should_codegen() {
126            let fn_rewrites = default_function_rewrites(session);
127            for rewrite in fn_rewrites {
128                rewrites.push(ModuleRewritePassAdapter::new(rewrite));
129            }
130        }
131    } else {
132        rewrites.extend(registered);
133    }
134
135    rewrites
136}
137
138pub fn default_function_rewrites(session: &Session) -> RewriteSet<hir::Function> {
139    use midenc_hir_transform as transforms;
140
141    // If no rewrites were explicitly enabled, and conversion to Miden Assembly is,
142    // then we must ensure that the basic transformation passes are applied.
143    //
144    // Otherwise, assume that the intent was to skip those rewrites and do not add them
145    let mut rewrites = RewriteSet::default();
146    if session.should_codegen() {
147        rewrites.push(transforms::SplitCriticalEdges);
148        rewrites.push(transforms::Treeify);
149        rewrites.push(transforms::InlineBlocks);
150        rewrites.push(transforms::ApplySpills);
151    }
152
153    rewrites
154}