Skip to main content

backend_lang/
compile.rs

1//! The Tier-1 path: an ir-lang 2.0 module to x86-64 machine code, with the
2//! symbols, relocations, data, and stack maps a linker or JIT needs.
3
4use alloc::collections::BTreeSet;
5use alloc::string::{String, ToString};
6use alloc::vec::Vec;
7use core::fmt;
8
9use ir_lang::{FuncId, Init, Linkage, Module, SymbolRef};
10
11use crate::isel::{self, IselError};
12use crate::mir;
13use crate::regalloc::{self, RegAllocError};
14use crate::target::abi::Abi;
15use crate::x86_64::{self, EmitError, EmittedFunction, Emitter, Program, Reloc, RelocKind};
16
17/// How to compile: the platform's C convention, frame and runtime choices,
18/// and selection and allocation settings.
19///
20/// The C convention (`abi`) serves ir-lang's `c` tag and its `fast` tag (the
21/// backend's own convention for code compiled together is the platform's C
22/// convention in this release). Runtime entry points are called by name and
23/// left as relocations unless the module defines them:
24///
25/// | Symbol | Called | With |
26/// |---|---|---|
27/// | `trap_symbol` | for `trap` and every trapping policy | the OPS code (first argument); never returns |
28/// | `gc_poll_symbol` | for a `safepoint` instruction | nothing; a safepoint with a stack map |
29/// | `resume_symbol` | for `resume` | the exception payload; never returns |
30/// | `pow_f64_symbol` | for an `f64` `pow` | the operands' bit patterns as two `u64` integer arguments; returns the result's bits as a `u64` |
31/// | `pow_f32_symbol` | for an `f32` `pow` | the operands' bit patterns as two `u32` integer arguments; returns the result's bits as a `u32` |
32///
33/// The two `pow` routines must compute ir-lang's `ls_pow` (OPS v2: the same
34/// bits on every tier, never a platform libm); for `f32`, `ls_pow` of both
35/// operands widened to `f64`, rounded once. In Rust, `ir_lang::ls_pow(
36/// f64::from_bits(x), f64::from_bits(y)).to_bits()`. They take bit patterns
37/// in integer registers because this release keeps floats in general-purpose
38/// registers (isa-lang 0.2 encodes no SSE).
39///
40/// # Examples
41///
42/// ```
43/// use backend_lang::Options;
44///
45/// let o = Options::sysv().with_frame_pointer(false);
46/// assert_eq!(o.abi.name(), "sysv64");
47/// assert!(!o.frame_pointer);
48/// assert_eq!(Options::win64().abi.name(), "win64");
49/// ```
50#[derive(Clone, Copy, Debug)]
51#[non_exhaustive]
52pub struct Options {
53    /// The platform's C convention, used for the `c` and `fast` tags and for
54    /// runtime calls.
55    pub abi: &'static Abi,
56    /// Whether functions keep a frame pointer (on by default). Without one,
57    /// `rbp` is one more register for the allocator.
58    pub frame_pointer: bool,
59    /// The runtime's trap handler.
60    pub trap_symbol: &'static str,
61    /// The runtime's GC poll, called by `safepoint`.
62    pub gc_poll_symbol: &'static str,
63    /// The runtime's unwinder entry, called by `resume`.
64    pub resume_symbol: &'static str,
65    /// The runtime's `ls_pow` for `f64` (bit patterns in, bit pattern out).
66    pub pow_f64_symbol: &'static str,
67    /// The runtime's `ls_pow` for `f32` (bit patterns in, bit pattern out).
68    pub pow_f32_symbol: &'static str,
69    /// The fewest cases a switch needs for a jump table.
70    pub jump_table_min_cases: u32,
71    /// A jump table is used when the case values span at most this many
72    /// table entries per case (3: at least a third of the entries are
73    /// cases).
74    pub jump_table_max_spread: u32,
75    /// Register-allocation settings.
76    pub regalloc: regalloc::Options,
77}
78
79impl Options {
80    /// For System V AMD64 platforms (Linux, the BSDs, macOS).
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use backend_lang::Options;
86    ///
87    /// assert_eq!(Options::sysv().trap_symbol, "__lexersketch_trap");
88    /// ```
89    #[must_use]
90    pub const fn sysv() -> Options {
91        Options::with_abi(&x86_64::SYSV)
92    }
93
94    /// For Windows x64.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use backend_lang::Options;
100    ///
101    /// assert_eq!(Options::win64().abi.name(), "win64");
102    /// ```
103    #[must_use]
104    pub const fn win64() -> Options {
105        Options::with_abi(&x86_64::WIN64)
106    }
107
108    /// For the platform this crate is compiled for: Win64 on Windows, System
109    /// V elsewhere.
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use backend_lang::Options;
115    ///
116    /// let name = Options::host().abi.name();
117    /// assert!(name == "sysv64" || name == "win64");
118    /// ```
119    #[must_use]
120    pub const fn host() -> Options {
121        if cfg!(windows) {
122            Options::win64()
123        } else {
124            Options::sysv()
125        }
126    }
127
128    /// The defaults with `abi` as the C convention.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use backend_lang::Options;
134    /// use backend_lang::x86_64::WIN64;
135    ///
136    /// assert_eq!(Options::with_abi(&WIN64).abi.name(), "win64");
137    /// ```
138    #[must_use]
139    pub const fn with_abi(abi: &'static Abi) -> Options {
140        Options {
141            abi,
142            frame_pointer: true,
143            trap_symbol: "__lexersketch_trap",
144            gc_poll_symbol: "__lexersketch_gc_poll",
145            resume_symbol: "__lexersketch_resume",
146            pow_f64_symbol: "__lexersketch_pow_f64",
147            pow_f32_symbol: "__lexersketch_pow_f32",
148            jump_table_min_cases: 4,
149            jump_table_max_spread: 3,
150            regalloc: regalloc::Options::new(),
151        }
152    }
153
154    /// Keeps or omits the frame pointer.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use backend_lang::Options;
160    ///
161    /// assert!(!Options::sysv().with_frame_pointer(false).frame_pointer);
162    /// ```
163    #[must_use]
164    pub const fn with_frame_pointer(mut self, keep: bool) -> Options {
165        self.frame_pointer = keep;
166        self
167    }
168
169    /// Renames the runtime entry points.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use backend_lang::Options;
175    ///
176    /// let o = Options::sysv().with_runtime("my_trap", "my_poll", "my_resume");
177    /// assert_eq!(o.gc_poll_symbol, "my_poll");
178    /// ```
179    #[must_use]
180    pub const fn with_runtime(
181        mut self,
182        trap: &'static str,
183        gc_poll: &'static str,
184        resume: &'static str,
185    ) -> Options {
186        self.trap_symbol = trap;
187        self.gc_poll_symbol = gc_poll;
188        self.resume_symbol = resume;
189        self
190    }
191
192    /// Renames the runtime's float `pow` routines (see [`Options`] for their
193    /// contract).
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// use backend_lang::Options;
199    ///
200    /// let o = Options::sysv().with_pow_runtime("my_pow", "my_powf");
201    /// assert_eq!((o.pow_f64_symbol, o.pow_f32_symbol), ("my_pow", "my_powf"));
202    /// assert_eq!(Options::sysv().pow_f64_symbol, "__lexersketch_pow_f64");
203    /// ```
204    #[must_use]
205    pub const fn with_pow_runtime(
206        mut self,
207        f64_pow: &'static str,
208        f32_pow: &'static str,
209    ) -> Options {
210        self.pow_f64_symbol = f64_pow;
211        self.pow_f32_symbol = f32_pow;
212        self
213    }
214
215    /// Sets when a switch becomes a jump table.
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use backend_lang::Options;
221    ///
222    /// // Never use a jump table.
223    /// let o = Options::sysv().with_jump_tables(u32::MAX, 1);
224    /// assert_eq!(o.jump_table_min_cases, u32::MAX);
225    /// ```
226    #[must_use]
227    pub const fn with_jump_tables(mut self, min_cases: u32, max_spread: u32) -> Options {
228        self.jump_table_min_cases = min_cases;
229        self.jump_table_max_spread = max_spread;
230        self
231    }
232
233    /// Sets the register-allocation settings.
234    ///
235    /// # Examples
236    ///
237    /// ```
238    /// use backend_lang::{regalloc, Options};
239    ///
240    /// let o = Options::sysv().with_regalloc(regalloc::Options::new().with_live_budget(1 << 20));
241    /// assert_eq!(o.regalloc.live_budget, 1 << 20);
242    /// ```
243    #[must_use]
244    pub const fn with_regalloc(mut self, regalloc: regalloc::Options) -> Options {
245        self.regalloc = regalloc;
246        self
247    }
248}
249
250impl Default for Options {
251    fn default() -> Options {
252        Options::host()
253    }
254}
255
256/// Why a module could not be compiled.
257///
258/// # Examples
259///
260/// ```
261/// use backend_lang::{CompileError, Options};
262/// use ir_lang::{Linkage, Module, Signature, Type};
263///
264/// let mut m = Module::new("m");
265/// let f = m.declare_function("f", &Signature::new(&[], &[Type::F32]), Linkage::Export)?;
266/// let mut b = m.build(f)?;
267/// let x = b.f32const(1.0)?;
268/// b.ret(&[x])?;
269/// let err = backend_lang::compile(&m, &Options::sysv()).unwrap_err();
270/// assert!(matches!(err, CompileError::Select { .. }));
271/// # Ok::<(), Box<dyn std::error::Error>>(())
272/// ```
273#[derive(Clone, Debug, PartialEq, Eq)]
274#[non_exhaustive]
275pub enum CompileError {
276    /// The module does not validate.
277    Invalid(ir_lang::ValidationError),
278    /// Instruction selection failed for a function.
279    Select {
280        /// The function.
281        function: String,
282        /// Why.
283        error: IselError,
284    },
285    /// Register allocation failed for a function.
286    Allocate {
287        /// The function.
288        function: String,
289        /// Why.
290        error: RegAllocError,
291    },
292    /// Emission failed (for a function, if one is named).
293    Emit {
294        /// The function, if the failure belongs to one.
295        function: Option<String>,
296        /// Why.
297        error: EmitError,
298    },
299}
300
301impl fmt::Display for CompileError {
302    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303        match self {
304            CompileError::Invalid(e) => write!(f, "invalid module: {e}"),
305            CompileError::Select { function, error } => write!(f, "`{function}`: {error}"),
306            CompileError::Allocate { function, error } => write!(f, "`{function}`: {error}"),
307            CompileError::Emit {
308                function: Some(n),
309                error,
310            } => write!(f, "`{n}`: {error}"),
311            CompileError::Emit {
312                function: None,
313                error,
314            } => fmt::Display::fmt(error, f),
315        }
316    }
317}
318
319impl core::error::Error for CompileError {
320    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
321        match self {
322            CompileError::Invalid(e) => Some(e),
323            CompileError::Select { error, .. } => Some(error),
324            CompileError::Allocate { error, .. } => Some(error),
325            CompileError::Emit { error, .. } => Some(error),
326        }
327    }
328}
329
330/// A global's bytes and the addresses stored in them.
331///
332/// # Examples
333///
334/// ```
335/// use backend_lang::Options;
336/// use ir_lang::{Global, Module};
337///
338/// let mut m = Module::new("m");
339/// let _ = m.declare_global("answer", Global::bytes(&[42, 0, 0, 0], 4))?;
340/// let c = backend_lang::compile(&m, &Options::sysv())?;
341/// let d = &c.data()[0];
342/// assert_eq!((d.name(), d.size(), d.align()), ("answer", 4, 4));
343/// assert_eq!(d.bytes(), Some(&[42u8, 0, 0, 0][..]));
344/// # Ok::<(), Box<dyn std::error::Error>>(())
345/// ```
346#[derive(Clone, Debug, PartialEq, Eq)]
347pub struct DataObject {
348    name: String,
349    size: u64,
350    align: u32,
351    mutable: bool,
352    exported: bool,
353    bytes: Option<Vec<u8>>,
354    relocs: Vec<Reloc>,
355}
356
357impl DataObject {
358    /// The global's symbol name.
359    ///
360    /// # Examples
361    ///
362    /// See [`DataObject`].
363    #[inline]
364    #[must_use]
365    pub fn name(&self) -> &str {
366        &self.name
367    }
368
369    /// Its size in bytes.
370    ///
371    /// # Examples
372    ///
373    /// See [`DataObject`].
374    #[inline]
375    #[must_use]
376    pub fn size(&self) -> u64 {
377        self.size
378    }
379
380    /// Its alignment in bytes.
381    ///
382    /// # Examples
383    ///
384    /// See [`DataObject`].
385    #[inline]
386    #[must_use]
387    pub fn align(&self) -> u32 {
388        self.align
389    }
390
391    /// Whether the program may write it (else it belongs in read-only data).
392    ///
393    /// # Examples
394    ///
395    /// See [`DataObject`].
396    #[inline]
397    #[must_use]
398    pub fn mutable(&self) -> bool {
399        self.mutable
400    }
401
402    /// Whether it is visible outside the module.
403    ///
404    /// # Examples
405    ///
406    /// See [`DataObject`].
407    #[inline]
408    #[must_use]
409    pub fn exported(&self) -> bool {
410        self.exported
411    }
412
413    /// Its initial bytes, or `None` for zero-filled data.
414    ///
415    /// # Examples
416    ///
417    /// See [`DataObject`].
418    #[inline]
419    #[must_use]
420    pub fn bytes(&self) -> Option<&[u8]> {
421        self.bytes.as_deref()
422    }
423
424    /// The addresses stored in it ([`RelocKind::Abs64`], offsets from its
425    /// start).
426    ///
427    /// # Examples
428    ///
429    /// See [`DataObject`].
430    #[inline]
431    #[must_use]
432    pub fn relocations(&self) -> &[Reloc] {
433        &self.relocs
434    }
435}
436
437/// A compiled module: machine code, every function's place and call-site
438/// records, the relocations against symbols defined elsewhere, and the
439/// module's data.
440///
441/// The code refers to globals and imports through relocations
442/// ([`relocations`](Compiled::relocations)); calls between the module's own
443/// functions are already resolved. A linker places the code and data and
444/// patches the relocations (object-lang writes them into an ELF relocatable
445/// as they are).
446///
447/// # Examples
448///
449/// ```
450/// use backend_lang::Options;
451/// use ir_lang::{Linkage, Module, Overflow, Signature, Type};
452///
453/// let mut m = Module::new("m");
454/// let f = m.declare_function("add", &Signature::new(&[Type::I64, Type::I64], &[Type::I64]), Linkage::Export)?;
455/// let mut b = m.build(f)?;
456/// let (x, y) = (b.param(0)?, b.param(1)?);
457/// let s = b.add(x, y, Overflow::Wrap)?;
458/// b.ret(&[s])?;
459///
460/// let c = backend_lang::compile(&m, &Options::sysv().with_frame_pointer(false))?;
461/// assert_eq!(c.function("add").unwrap().offset(), 0);
462/// assert!(c.relocations().is_empty());
463/// assert_eq!(c.code().last(), Some(&0xC3)); // ret
464/// # Ok::<(), Box<dyn std::error::Error>>(())
465/// ```
466#[derive(Clone, Debug, PartialEq, Eq)]
467pub struct Compiled {
468    program: Program,
469    data: Vec<DataObject>,
470    imports: Vec<String>,
471    exports: Vec<String>,
472}
473
474impl Compiled {
475    /// The machine code of every function, back to back.
476    ///
477    /// # Examples
478    ///
479    /// See [`Compiled`].
480    #[inline]
481    #[must_use]
482    pub fn code(&self) -> &[u8] {
483        self.program.bytes()
484    }
485
486    /// The emitted program (code, functions, relocations).
487    ///
488    /// # Examples
489    ///
490    /// See [`Compiled`].
491    #[inline]
492    #[must_use]
493    pub fn program(&self) -> &Program {
494        &self.program
495    }
496
497    /// Every function, in module order.
498    ///
499    /// # Examples
500    ///
501    /// See [`Compiled`].
502    #[inline]
503    #[must_use]
504    pub fn functions(&self) -> &[EmittedFunction] {
505        self.program.functions()
506    }
507
508    /// The function named `name`.
509    ///
510    /// # Examples
511    ///
512    /// See [`Compiled`].
513    #[must_use]
514    pub fn function(&self, name: &str) -> Option<&EmittedFunction> {
515        // The symbol table is sorted by name, and functions sit in emission
516        // order at increasing offsets: two binary searches, not a scan.
517        let offset = self.program.symbol(name)?;
518        let functions = self.program.functions();
519        let i = functions.partition_point(|f| f.offset() < offset);
520        functions.get(i).filter(|f| f.name() == name)
521    }
522
523    /// The code's relocations against globals, imports, and runtime entry
524    /// points.
525    ///
526    /// # Examples
527    ///
528    /// See [`Compiled`].
529    #[inline]
530    #[must_use]
531    pub fn relocations(&self) -> &[Reloc] {
532        self.program.relocations()
533    }
534
535    /// The module's globals (imported globals excluded).
536    ///
537    /// # Examples
538    ///
539    /// See [`DataObject`].
540    #[inline]
541    #[must_use]
542    pub fn data(&self) -> &[DataObject] {
543        &self.data
544    }
545
546    /// The symbols the code and data refer to that the module does not
547    /// define (imports and runtime entry points), in name order.
548    ///
549    /// # Examples
550    ///
551    /// ```
552    /// use backend_lang::Options;
553    /// use ir_lang::{Linkage, Module, Signature};
554    ///
555    /// let mut m = Module::new("m");
556    /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Export)?;
557    /// let mut b = m.build(f)?;
558    /// b.trap(1)?;
559    /// let c = backend_lang::compile(&m, &Options::sysv())?;
560    /// assert_eq!(c.imports(), ["__lexersketch_trap"]);
561    /// # Ok::<(), Box<dyn std::error::Error>>(())
562    /// ```
563    #[inline]
564    #[must_use]
565    pub fn imports(&self) -> &[String] {
566        &self.imports
567    }
568
569    /// The functions and globals with `export` linkage, in name order.
570    ///
571    /// # Examples
572    ///
573    /// See [`Compiled::imports`].
574    #[inline]
575    #[must_use]
576    pub fn exports(&self) -> &[String] {
577        &self.exports
578    }
579}
580
581/// Compiles every function of `module` to x86-64 machine code: instruction
582/// selection, register allocation, and emission.
583///
584/// # Errors
585///
586/// [`CompileError`], naming the function that failed.
587///
588/// # Examples
589///
590/// See [`Compiled`].
591pub fn compile(module: &Module, options: &Options) -> Result<Compiled, CompileError> {
592    module.validate().map_err(CompileError::Invalid)?;
593    let mut defined: BTreeSet<String> = BTreeSet::new();
594    let mut exports: Vec<String> = Vec::new();
595    for func in module.functions() {
596        if let Some(d) = module.func_decl(func) {
597            if d.linkage() != Linkage::Import {
598                let _ = defined.insert(d.name().to_string());
599                if d.linkage() == Linkage::Export {
600                    exports.push(d.name().to_string());
601                }
602            }
603        }
604    }
605    for g in module.globals() {
606        if let (Some(name), Some(data)) = (module.global_name(g), module.global(g)) {
607            if data.linkage != Linkage::Import {
608                let _ = defined.insert(name.to_string());
609                if data.linkage == Linkage::Export {
610                    exports.push(name.to_string());
611                }
612            }
613        }
614    }
615    exports.sort();
616
617    let mut emitter = Emitter::new();
618    let emit_err = |function: Option<&str>, error: EmitError| CompileError::Emit {
619        function: function.map(str::to_string),
620        error,
621    };
622    // Everything not defined here is reached through a relocation:
623    // imports, globals (data lives apart from code), runtime entry points.
624    let mut externals: BTreeSet<String> = BTreeSet::new();
625    for func in module.functions() {
626        if let Some(d) = module.func_decl(func) {
627            if d.linkage() == Linkage::Import {
628                let _ = externals.insert(d.name().to_string());
629            }
630        }
631    }
632    for g in module.globals() {
633        if let Some(name) = module.global_name(g) {
634            let _ = externals.insert(name.to_string());
635        }
636    }
637    for rt in [
638        options.trap_symbol,
639        options.gc_poll_symbol,
640        options.resume_symbol,
641        options.pow_f64_symbol,
642        options.pow_f32_symbol,
643    ] {
644        if !defined.contains(rt) {
645            let _ = externals.insert(rt.to_string());
646        }
647    }
648    for name in &externals {
649        emitter
650            .declare_external(name)
651            .map_err(|e| emit_err(Some(name), e))?;
652    }
653
654    for func in module.functions() {
655        let Some(decl) = module.func_decl(func) else {
656            continue;
657        };
658        if decl.linkage() == Linkage::Import {
659            continue;
660        }
661        let name = decl.name();
662        let mir = compile_validated(module, func, options)?;
663        let _ = emitter
664            .function(&mir)
665            .map_err(|e| emit_err(Some(name), e))?;
666    }
667    let program = emitter.finish().map_err(|e| emit_err(None, e))?;
668
669    let mut data = Vec::new();
670    for g in module.globals() {
671        let (Some(name), Some(gd)) = (module.global_name(g), module.global(g)) else {
672            continue;
673        };
674        if gd.linkage == Linkage::Import {
675            continue;
676        }
677        let mut relocs = Vec::with_capacity(gd.relocs.len());
678        for r in &gd.relocs {
679            let target = match r.target {
680                SymbolRef::Func(f) => module.func_decl(f).map(|d| d.name().to_string()),
681                SymbolRef::Global(t) => module.global_name(t).map(str::to_string),
682                _ => None,
683            };
684            if let Some(t) = target {
685                relocs.push(Reloc::new(
686                    r.offset as usize,
687                    RelocKind::Abs64,
688                    &t,
689                    r.addend,
690                ));
691            }
692        }
693        data.push(DataObject {
694            name: name.to_string(),
695            size: gd.size,
696            align: gd.align,
697            mutable: gd.mutable,
698            exported: gd.linkage == Linkage::Export,
699            bytes: match &gd.init {
700                Init::Bytes(b) => Some(b.clone()),
701                _ => None,
702            },
703            relocs,
704        });
705    }
706
707    let mut imports: BTreeSet<String> = BTreeSet::new();
708    for r in program.relocations() {
709        if !defined.contains(&r.symbol) {
710            let _ = imports.insert(r.symbol.clone());
711        }
712    }
713    for d in &data {
714        for r in d.relocations() {
715            if !defined.contains(&r.symbol) {
716                let _ = imports.insert(r.symbol.clone());
717            }
718        }
719    }
720    Ok(Compiled {
721        program,
722        data,
723        imports: imports.into_iter().collect(),
724        exports,
725    })
726}
727
728/// Selects and allocates one function: the MIR on physical registers that
729/// [`emit`](crate::emit) or an [`Emitter`] encodes.
730///
731/// # Errors
732///
733/// [`CompileError`].
734///
735/// # Examples
736///
737/// ```
738/// use backend_lang::Options;
739/// use ir_lang::{Linkage, Module, Signature, Type};
740///
741/// let mut m = Module::new("m");
742/// let f = m.declare_function("seven", &Signature::new(&[], &[Type::I32]), Linkage::Export)?;
743/// let mut b = m.build(f)?;
744/// let k = b.iconst(Type::I32, 7)?;
745/// b.ret(&[k])?;
746/// let mir = backend_lang::compile_function(&m, f, &Options::sysv())?;
747/// assert!(mir.to_string().contains("rax"));
748/// # Ok::<(), Box<dyn std::error::Error>>(())
749/// ```
750pub fn compile_function(
751    module: &Module,
752    func: FuncId,
753    options: &Options,
754) -> Result<mir::Function, CompileError> {
755    module.validate().map_err(CompileError::Invalid)?;
756    compile_validated(module, func, options)
757}
758
759fn compile_validated(
760    module: &Module,
761    func: FuncId,
762    options: &Options,
763) -> Result<mir::Function, CompileError> {
764    let name = module
765        .func_decl(func)
766        .map_or_else(|| func.to_string(), |d| d.name().to_string());
767    let mut mir =
768        isel::select_validated(module, func, options).map_err(|error| CompileError::Select {
769            function: name.clone(),
770            error,
771        })?;
772    let _ = regalloc::allocate_with(&mut mir, &options.regalloc).map_err(|error| {
773        CompileError::Allocate {
774            function: name.clone(),
775            error,
776        }
777    })?;
778    Ok(mir)
779}