midenc_compile/
lib.rs

1#![no_std]
2#![deny(warnings)]
3
4extern crate alloc;
5#[cfg(feature = "std")]
6extern crate std;
7
8mod compiler;
9mod stage;
10mod stages;
11
12use alloc::{rc::Rc, vec::Vec};
13
14pub use midenc_hir::Context;
15use midenc_hir::Op;
16use midenc_session::{
17    OutputMode,
18    diagnostics::{Diagnostic, Report, WrapErr, miette},
19};
20
21pub use self::{
22    compiler::Compiler,
23    stages::{CodegenOutput, LinkOutput},
24};
25use self::{stage::Stage, stages::*};
26
27pub type CompilerResult<T> = Result<T, Report>;
28
29/// The compilation pipeline was stopped early
30#[derive(Debug, thiserror::Error, Diagnostic)]
31#[error("compilation was canceled by user")]
32#[diagnostic()]
33pub struct CompilerStopped;
34
35/// Run the compiler using the provided [Session]
36pub fn compile(context: Rc<Context>) -> CompilerResult<()> {
37    use midenc_hir::formatter::DisplayHex;
38
39    log::info!("starting compilation session");
40
41    midenc_codegen_masm::register_dialect_hooks(&context);
42
43    let session = context.session();
44    match compile_inputs(session.inputs.clone(), context.clone())? {
45        Artifact::Assembled(ref package) => {
46            log::info!(
47                "succesfully assembled mast package '{}' with digest {}",
48                package.name,
49                DisplayHex::new(&package.digest().as_bytes())
50            );
51            session
52                .emit(OutputMode::Text, package)
53                .map_err(Report::msg)
54                .wrap_err("failed to pretty print 'mast' artifact")?;
55            session
56                .emit(OutputMode::Binary, package)
57                .map_err(Report::msg)
58                .wrap_err("failed to serialize 'mast' artifact")
59        }
60        Artifact::Lowered(_) => {
61            log::debug!("no outputs requested by user: pipeline stopped before assembly");
62            Ok(())
63        }
64    }
65}
66
67/// Same as `compile`, but return compiled artifacts to the caller
68pub fn compile_to_memory(context: Rc<Context>) -> CompilerResult<Artifact> {
69    let inputs = context.session().inputs.clone();
70    compile_inputs(inputs, context)
71}
72
73/// Same as `compile_to_memory`, but allows registering a callback which will be used as an extra
74/// compiler stage immediately after code generation and prior to assembly, if the linker was run.
75pub fn compile_to_memory_with_pre_assembly_stage<F>(
76    context: Rc<Context>,
77    pre_assembly_stage: &mut F,
78) -> CompilerResult<Artifact>
79where
80    F: FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput>,
81{
82    let mut stages = ParseStage
83        .collect(LinkStage)
84        .next_optional(ApplyRewritesStage)
85        .next(CodegenStage)
86        .next(
87            pre_assembly_stage
88                as &mut (
89                         dyn FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput> + '_
90                     ),
91        )
92        .next(AssembleStage);
93
94    let inputs = context.session().inputs.clone();
95    stages.run(inputs, context)
96}
97
98/// Compile the current inputs without lowering to Miden Assembly.
99///
100/// Returns the translated pre-link outputs of the compiler's link stage.
101pub fn compile_to_optimized_hir(context: Rc<Context>) -> CompilerResult<LinkOutput> {
102    let mut stages = ParseStage.collect(LinkStage).next_optional(ApplyRewritesStage);
103
104    let inputs = context.session().inputs.clone();
105    stages.run(inputs, context)
106}
107
108/// Compile the current inputs without lowering to Miden Assembly and without any IR transformations.
109///
110/// Returns the translated pre-link outputs of the compiler's link stage.
111pub fn compile_to_unoptimized_hir(context: Rc<Context>) -> CompilerResult<LinkOutput> {
112    let mut stages = ParseStage.collect(LinkStage);
113
114    let inputs = context.session().inputs.clone();
115    stages.run(inputs, context)
116}
117
118/// Lowers previously-generated pre-link outputs of the compiler to Miden Assembly/MAST.
119///
120/// Returns the compiled artifact, just like `compile_to_memory` would.
121pub fn compile_link_output_to_masm(link_output: LinkOutput) -> CompilerResult<Artifact> {
122    let mut stages = CodegenStage.next(AssembleStage);
123
124    let context = link_output.component.borrow().as_operation().context_rc();
125    stages.run(link_output, context)
126}
127
128/// Lowers previously-generated pre-link outputs of the compiler to Miden Assembly/MAST, but with
129/// an provided callback that will be used as an extra compiler stage just prior to assembly.
130///
131/// Returns the compiled artifact, just like `compile_to_memory` would.
132pub fn compile_link_output_to_masm_with_pre_assembly_stage<F>(
133    link_output: LinkOutput,
134    pre_assembly_stage: &mut F,
135) -> CompilerResult<Artifact>
136where
137    F: FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput>,
138{
139    let mut stages = CodegenStage
140        .next(
141            pre_assembly_stage
142                as &mut (
143                         dyn FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput> + '_
144                     ),
145        )
146        .next(AssembleStage);
147
148    let context = link_output.component.borrow().as_operation().context_rc();
149    stages.run(link_output, context)
150}
151
152fn compile_inputs(
153    inputs: Vec<midenc_session::InputFile>,
154    context: Rc<Context>,
155) -> CompilerResult<Artifact> {
156    let mut stages = ParseStage
157        .collect(LinkStage)
158        .next_optional(ApplyRewritesStage)
159        .next(CodegenStage)
160        .next(AssembleStage);
161
162    stages.run(inputs, context)
163}