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    diagnostics::{miette, Diagnostic, Report, WrapErr},
18    OutputMode,
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 (dyn FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput>
89                          + '_),
90        )
91        .next(AssembleStage);
92
93    let inputs = context.session().inputs.clone();
94    stages.run(inputs, context)
95}
96
97/// Compile the current inputs without lowering to Miden Assembly.
98///
99/// Returns the translated pre-link outputs of the compiler's link stage.
100pub fn compile_to_optimized_hir(context: Rc<Context>) -> CompilerResult<LinkOutput> {
101    let mut stages = ParseStage.collect(LinkStage).next_optional(ApplyRewritesStage);
102
103    let inputs = context.session().inputs.clone();
104    stages.run(inputs, context)
105}
106
107/// Compile the current inputs without lowering to Miden Assembly and without any IR transformations.
108///
109/// Returns the translated pre-link outputs of the compiler's link stage.
110pub fn compile_to_unoptimized_hir(context: Rc<Context>) -> CompilerResult<LinkOutput> {
111    let mut stages = ParseStage.collect(LinkStage);
112
113    let inputs = context.session().inputs.clone();
114    stages.run(inputs, context)
115}
116
117/// Lowers previously-generated pre-link outputs of the compiler to Miden Assembly/MAST.
118///
119/// Returns the compiled artifact, just like `compile_to_memory` would.
120pub fn compile_link_output_to_masm(link_output: LinkOutput) -> CompilerResult<Artifact> {
121    let mut stages = CodegenStage.next(AssembleStage);
122
123    let context = link_output.component.borrow().as_operation().context_rc();
124    stages.run(link_output, context)
125}
126
127/// Lowers previously-generated pre-link outputs of the compiler to Miden Assembly/MAST, but with
128/// an provided callback that will be used as an extra compiler stage just prior to assembly.
129///
130/// Returns the compiled artifact, just like `compile_to_memory` would.
131pub fn compile_link_output_to_masm_with_pre_assembly_stage<F>(
132    link_output: LinkOutput,
133    pre_assembly_stage: &mut F,
134) -> CompilerResult<Artifact>
135where
136    F: FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput>,
137{
138    let mut stages = CodegenStage
139        .next(
140            pre_assembly_stage
141                as &mut (dyn FnMut(CodegenOutput, Rc<Context>) -> CompilerResult<CodegenOutput>
142                          + '_),
143        )
144        .next(AssembleStage);
145
146    let context = link_output.component.borrow().as_operation().context_rc();
147    stages.run(link_output, context)
148}
149
150fn compile_inputs(
151    inputs: Vec<midenc_session::InputFile>,
152    context: Rc<Context>,
153) -> CompilerResult<Artifact> {
154    let mut stages = ParseStage
155        .collect(LinkStage)
156        .next_optional(ApplyRewritesStage)
157        .next(CodegenStage)
158        .next(AssembleStage);
159
160    stages.run(inputs, context)
161}