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}