1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
use alloc::{fmt, str::FromStr, string::String, sync::Arc, vec, vec::Vec};
use std::path::{Path, PathBuf};

use crate::{
    diagnostics::{DiagnosticsConfig, Emitter},
    ColorChoice, CompileFlags, LinkLibrary, OutputTypes, ProjectType, TargetEnv,
};

/// This struct contains all of the configuration options for the compiler
#[derive(Debug)]
pub struct Options {
    /// The name of the program being compiled
    pub name: Option<String>,
    /// The type of project we're compiling this session
    pub project_type: ProjectType,
    /// The name of the function to call as the entrypoint
    pub entrypoint: Option<String>,
    /// The current target environment for this session
    pub target: TargetEnv,
    /// The optimization level for the current program
    pub optimize: OptLevel,
    /// The level of debugging info for the current program
    pub debug: DebugInfo,
    /// The type of outputs to emit
    pub output_types: OutputTypes,
    /// The paths in which to search for Miden Assembly libraries to link against
    pub search_paths: Vec<PathBuf>,
    /// The set of Miden libraries to link against
    pub link_libraries: Vec<LinkLibrary>,
    /// The location of the libraries which are shipped with the compiler
    pub sysroot: Option<PathBuf>,
    /// Whether, and how, to color terminal output
    pub color: ColorChoice,
    /// The current diagnostics configuration
    pub diagnostics: DiagnosticsConfig,
    /// The current working directory of the compiler
    pub current_dir: PathBuf,
    /// Only parse inputs
    pub parse_only: bool,
    /// Only perform semantic analysis on the input
    pub analyze_only: bool,
    /// Run the linker on the inputs, but do not generate Miden Assembly
    pub link_only: bool,
    /// Generate Miden Assembly from the inputs without the linker
    pub no_link: bool,
    /// Print CFG to stdout after each pass
    pub print_cfg_after_all: bool,
    /// Print CFG to stdout each time the named passes are applied
    pub print_cfg_after_pass: Vec<String>,
    /// Print IR to stdout after each pass
    pub print_ir_after_all: bool,
    /// Print IR to stdout each time the named passes are applied
    pub print_ir_after_pass: Vec<String>,
    /// Save intermediate artifacts in memory during compilation
    pub save_temps: bool,
    /// We store any leftover argument matches in the session options for use
    /// by any downstream crates that register custom flags
    pub flags: CompileFlags,
}

impl Default for Options {
    fn default() -> Self {
        let current_dir = std::env::current_dir().expect("could not get working directory");
        let target = TargetEnv::default();
        let project_type = ProjectType::default_for_target(target);
        Self::new(None, target, project_type, current_dir, None)
    }
}

impl Options {
    pub fn new(
        name: Option<String>,
        target: TargetEnv,
        project_type: ProjectType,
        current_dir: PathBuf,
        sysroot: Option<PathBuf>,
    ) -> Self {
        let sysroot = sysroot.or_else(|| {
            std::env::var("HOME").ok().map(|home| {
                Path::new(&home)
                    .join(".miden")
                    .join("toolchains")
                    .join(crate::MIDENC_BUILD_VERSION)
            })
        });

        Self {
            name,
            target,
            project_type,
            entrypoint: None,
            optimize: OptLevel::None,
            debug: DebugInfo::None,
            output_types: Default::default(),
            search_paths: vec![],
            link_libraries: vec![],
            sysroot,
            color: Default::default(),
            diagnostics: Default::default(),
            current_dir,
            parse_only: false,
            analyze_only: false,
            link_only: false,
            no_link: false,
            save_temps: false,
            print_cfg_after_all: false,
            print_cfg_after_pass: vec![],
            print_ir_after_all: false,
            print_ir_after_pass: vec![],
            flags: CompileFlags::default(),
        }
    }

    #[inline(always)]
    pub fn with_color(mut self, color: ColorChoice) -> Self {
        self.color = color;
        self
    }

    #[inline(always)]
    pub fn with_verbosity(mut self, verbosity: Verbosity) -> Self {
        self.diagnostics.verbosity = verbosity;
        self
    }

    #[inline(always)]
    pub fn with_debug_info(mut self, debug: DebugInfo) -> Self {
        self.debug = debug;
        self
    }

    #[inline(always)]
    pub fn with_optimization(mut self, level: OptLevel) -> Self {
        self.optimize = level;
        self
    }

    pub fn with_warnings(mut self, warnings: Warnings) -> Self {
        self.diagnostics.warnings = warnings;
        self
    }

    #[inline(always)]
    pub fn with_output_types(mut self, output_types: OutputTypes) -> Self {
        self.output_types = output_types;
        self
    }

    #[doc(hidden)]
    pub fn with_extra_flags(mut self, flags: CompileFlags) -> Self {
        self.flags = flags;
        self
    }

    #[doc(hidden)]
    pub fn set_extra_flags(&mut self, flags: CompileFlags) {
        self.flags = flags;
    }

    /// Get a new [Emitter] based on the current options.
    pub fn default_emitter(&self) -> Arc<dyn Emitter> {
        use crate::diagnostics::{DefaultEmitter, NullEmitter};

        match self.diagnostics.verbosity {
            Verbosity::Silent => Arc::new(NullEmitter::new(self.color)),
            _ => Arc::new(DefaultEmitter::new(self.color)),
        }
    }

    /// Returns true if source location information should be emitted by the compiler
    #[inline(always)]
    pub fn emit_source_locations(&self) -> bool {
        matches!(self.debug, DebugInfo::Line | DebugInfo::Full)
    }

    /// Returns true if rich debugging information should be emitted by the compiler
    #[inline(always)]
    pub fn emit_debug_decorators(&self) -> bool {
        matches!(self.debug, DebugInfo::Full)
    }

    /// Returns true if debug assertions are enabled
    #[inline(always)]
    pub fn emit_debug_assertions(&self) -> bool {
        self.debug != DebugInfo::None && matches!(self.optimize, OptLevel::None | OptLevel::Basic)
    }
}

/// This enum describes the degree to which compiled programs will be optimized
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum OptLevel {
    /// No optimizations at all
    None,
    /// Only basic optimizations are applied, e.g. constant propagation
    Basic,
    /// Most optimizations are applied, except when the cost is particularly high.
    #[default]
    Balanced,
    /// All optimizations are applied, with all tradeoffs in favor of runtime performance
    Max,
    /// Most optimizations are applied, but tuned to trade runtime performance for code size
    Size,
    /// Only optimizations which reduce code size are applied
    SizeMin,
}

/// This enum describes what type of debugging information to emit in compiled programs
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum DebugInfo {
    /// Do not emit debug info in the final output
    None,
    /// Emit source location information in the final output
    #[default]
    Line,
    /// Emit all available debug information in the final output
    Full,
}

/// This enum represents the behavior of the compiler with regard to warnings
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum Warnings {
    /// Disable all warnings
    None,
    /// Enable all warnings
    #[default]
    All,
    /// Promotes warnings to errors
    Error,
}
impl fmt::Display for Warnings {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::None => f.write_str("none"),
            Self::All => f.write_str("auto"),
            Self::Error => f.write_str("error"),
        }
    }
}
impl FromStr for Warnings {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "none" => Ok(Self::None),
            "all" => Ok(Self::All),
            "error" => Ok(Self::Error),
            _ => Err(()),
        }
    }
}

/// This enum represents the type of messages produced by the compiler during execution
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
pub enum Verbosity {
    /// Emit additional debug/trace information during compilation
    Debug,
    /// Emit the standard informational, warning, and error messages
    #[default]
    Info,
    /// Only emit warnings and errors
    Warning,
    /// Only emit errors
    Error,
    /// Do not emit anything to stdout/stderr
    Silent,
}