wasmer_compiler_llvm/
compiler.rs

1use crate::CompiledKind;
2use crate::config::LLVM;
3use crate::trampoline::FuncTrampoline;
4use crate::translator::FuncTranslator;
5use inkwell::DLLStorageClass;
6use inkwell::context::Context;
7use inkwell::memory_buffer::MemoryBuffer;
8use inkwell::module::{Linkage, Module};
9use inkwell::targets::FileType;
10use rayon::ThreadPoolBuilder;
11use rayon::iter::ParallelBridge;
12use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
13use std::borrow::Cow;
14use std::collections::{HashMap, HashSet};
15use std::sync::Arc;
16use wasmer_compiler::types::function::{Compilation, UnwindInfo};
17use wasmer_compiler::types::module::CompileModuleInfo;
18use wasmer_compiler::types::relocation::RelocationKind;
19use wasmer_compiler::{
20    Compiler, FunctionBodyData, ModuleMiddleware, ModuleTranslationState,
21    types::{
22        relocation::RelocationTarget,
23        section::{CustomSection, CustomSectionProtection, SectionBody, SectionIndex},
24        symbols::{Symbol, SymbolRegistry},
25    },
26};
27use wasmer_types::entity::{EntityRef, PrimaryMap};
28use wasmer_types::target::Target;
29use wasmer_types::{CompileError, FunctionIndex, LocalFunctionIndex, ModuleInfo, SignatureIndex};
30use wasmer_vm::LibCall;
31
32/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
33/// optimizing it and then translating to assembly.
34#[derive(Debug)]
35pub struct LLVMCompiler {
36    config: LLVM,
37}
38
39impl LLVMCompiler {
40    /// Creates a new LLVM compiler
41    pub fn new(config: LLVM) -> LLVMCompiler {
42        LLVMCompiler { config }
43    }
44
45    /// Gets the config for this Compiler
46    fn config(&self) -> &LLVM {
47        &self.config
48    }
49}
50
51struct ShortNames {}
52
53impl SymbolRegistry for ShortNames {
54    fn symbol_to_name(&self, symbol: Symbol) -> String {
55        match symbol {
56            Symbol::Metadata => "M".to_string(),
57            Symbol::LocalFunction(index) => format!("f{}", index.index()),
58            Symbol::Section(index) => format!("s{}", index.index()),
59            Symbol::FunctionCallTrampoline(index) => format!("t{}", index.index()),
60            Symbol::DynamicFunctionTrampoline(index) => format!("d{}", index.index()),
61        }
62    }
63
64    fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
65        if name.len() < 2 {
66            return None;
67        }
68        let (ty, idx) = name.split_at(1);
69        if ty.starts_with('M') {
70            return Some(Symbol::Metadata);
71        }
72
73        let idx = idx.parse::<u32>().ok()?;
74        match ty.chars().next().unwrap() {
75            'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))),
76            's' => Some(Symbol::Section(SectionIndex::from_u32(idx))),
77            't' => Some(Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(
78                idx,
79            ))),
80            'd' => Some(Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(
81                idx,
82            ))),
83            _ => None,
84        }
85    }
86}
87
88struct ModuleBasedSymbolRegistry {
89    wasm_module: Arc<ModuleInfo>,
90    local_func_names: HashMap<String, LocalFunctionIndex>,
91    short_names: ShortNames,
92}
93
94impl ModuleBasedSymbolRegistry {
95    const PROBLEMATIC_PREFIXES: &[&'static str] = &[
96        ".L",    // .L is used for local symbols
97        "llvm.", // llvm. is used for LLVM's own intrinsics
98    ];
99
100    fn new(wasm_module: Arc<ModuleInfo>) -> Self {
101        let local_func_names = HashMap::from_iter(
102            wasm_module
103                .function_names
104                .iter()
105                .map(|(f, v)| (wasm_module.local_func_index(*f), v))
106                .filter(|(f, _)| f.is_some())
107                .map(|(f, v)| (format!("{}_{}", v.clone(), f.unwrap().as_u32()), f.unwrap())),
108        );
109        Self {
110            wasm_module,
111            local_func_names,
112            short_names: ShortNames {},
113        }
114    }
115
116    // If the name starts with a problematic prefix, we prefix it with an underscore.
117    fn fixup_problematic_name(name: &str) -> Cow<str> {
118        for prefix in Self::PROBLEMATIC_PREFIXES {
119            if name.starts_with(prefix) {
120                return format!("_{name}").into();
121            }
122        }
123        name.into()
124    }
125
126    // If the name starts with an underscore and the rest starts with a problematic prefix,
127    // remove the underscore to get back the original name. This is necessary to be able
128    // to match the name back to the original name in the wasm module.
129    fn unfixup_problematic_name(name: &str) -> &str {
130        if let Some(stripped_name) = name.strip_prefix('_') {
131            for prefix in Self::PROBLEMATIC_PREFIXES {
132                if stripped_name.starts_with(prefix) {
133                    return stripped_name;
134                }
135            }
136        }
137
138        name
139    }
140}
141
142impl SymbolRegistry for ModuleBasedSymbolRegistry {
143    fn symbol_to_name(&self, symbol: Symbol) -> String {
144        match symbol {
145            Symbol::LocalFunction(index) => self
146                .wasm_module
147                .function_names
148                .get(&self.wasm_module.func_index(index))
149                .map(|name| format!("{}_{}", Self::fixup_problematic_name(name), index.as_u32()))
150                .unwrap_or(self.short_names.symbol_to_name(symbol)),
151            _ => self.short_names.symbol_to_name(symbol),
152        }
153    }
154
155    fn name_to_symbol(&self, name: &str) -> Option<Symbol> {
156        let name = Self::unfixup_problematic_name(name);
157        if let Some(idx) = self.local_func_names.get(name) {
158            Some(Symbol::LocalFunction(*idx))
159        } else {
160            self.short_names.name_to_symbol(name)
161        }
162    }
163}
164
165impl LLVMCompiler {
166    #[allow(clippy::too_many_arguments)]
167    fn compile_native_object(
168        &self,
169        target: &Target,
170        compile_info: &CompileModuleInfo,
171        module_translation: &ModuleTranslationState,
172        function_body_inputs: &PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
173        symbol_registry: &dyn SymbolRegistry,
174        wasmer_metadata: &[u8],
175        binary_format: target_lexicon::BinaryFormat,
176    ) -> Result<Vec<u8>, CompileError> {
177        let target_machine = self.config().target_machine(target);
178        let ctx = Context::create();
179
180        // TODO: https:/github.com/rayon-rs/rayon/issues/822
181
182        let merged_bitcode = function_body_inputs.into_iter().par_bridge().map_init(
183            || {
184                let target_machine = self.config().target_machine(target);
185                FuncTranslator::new(target_machine, binary_format).unwrap()
186            },
187            |func_translator, (i, input)| {
188                let module = func_translator.translate_to_module(
189                    &compile_info.module,
190                    module_translation,
191                    &i,
192                    input,
193                    self.config(),
194                    &compile_info.memory_styles,
195                    &compile_info.table_styles,
196                    symbol_registry,
197                )?;
198
199                Ok(module.write_bitcode_to_memory().as_slice().to_vec())
200            },
201        );
202
203        let trampolines_bitcode = compile_info.module.signatures.iter().par_bridge().map_init(
204            || {
205                let target_machine = self.config().target_machine(target);
206                FuncTrampoline::new(target_machine, binary_format).unwrap()
207            },
208            |func_trampoline, (i, sig)| {
209                let name = symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(i));
210                let module = func_trampoline.trampoline_to_module(
211                    sig,
212                    self.config(),
213                    &name,
214                    compile_info,
215                )?;
216                Ok(module.write_bitcode_to_memory().as_slice().to_vec())
217            },
218        );
219
220        let dynamic_trampolines_bitcode =
221            compile_info.module.functions.iter().par_bridge().map_init(
222                || {
223                    let target_machine = self.config().target_machine(target);
224                    (
225                        FuncTrampoline::new(target_machine, binary_format).unwrap(),
226                        &compile_info.module.signatures,
227                    )
228                },
229                |(func_trampoline, signatures), (i, sig)| {
230                    let sig = &signatures[*sig];
231                    let name = symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(i));
232                    let module =
233                        func_trampoline.dynamic_trampoline_to_module(sig, self.config(), &name)?;
234                    Ok(module.write_bitcode_to_memory().as_slice().to_vec())
235                },
236            );
237
238        let merged_bitcode = merged_bitcode
239            .chain(trampolines_bitcode)
240            .chain(dynamic_trampolines_bitcode)
241            .collect::<Result<Vec<_>, CompileError>>()?
242            .into_par_iter()
243            .reduce_with(|bc1, bc2| {
244                let ctx = Context::create();
245                let membuf = MemoryBuffer::create_from_memory_range(&bc1, "");
246                let m1 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap();
247                let membuf = MemoryBuffer::create_from_memory_range(&bc2, "");
248                let m2 = Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap();
249                m1.link_in_module(m2).unwrap();
250                m1.write_bitcode_to_memory().as_slice().to_vec()
251            });
252        let merged_module = if let Some(bc) = merged_bitcode {
253            let membuf = MemoryBuffer::create_from_memory_range(&bc, "");
254            Module::parse_bitcode_from_buffer(&membuf, &ctx).unwrap()
255        } else {
256            ctx.create_module("")
257        };
258
259        let i8_ty = ctx.i8_type();
260        let metadata_init = i8_ty.const_array(
261            wasmer_metadata
262                .iter()
263                .map(|v| i8_ty.const_int(*v as u64, false))
264                .collect::<Vec<_>>()
265                .as_slice(),
266        );
267        let metadata_gv = merged_module.add_global(
268            metadata_init.get_type(),
269            None,
270            &symbol_registry.symbol_to_name(wasmer_compiler::types::symbols::Symbol::Metadata),
271        );
272        metadata_gv.set_initializer(&metadata_init);
273        metadata_gv.set_linkage(Linkage::DLLExport);
274        metadata_gv.set_dll_storage_class(DLLStorageClass::Export);
275        metadata_gv.set_alignment(16);
276
277        if self.config().enable_verifier {
278            merged_module.verify().unwrap();
279        }
280
281        let memory_buffer = target_machine
282            .write_to_memory_buffer(&merged_module, FileType::Object)
283            .unwrap();
284        if let Some(ref callbacks) = self.config.callbacks {
285            callbacks.obj_memory_buffer(&CompiledKind::Module, &memory_buffer);
286        }
287
288        tracing::trace!("Finished compling the module!");
289        Ok(memory_buffer.as_slice().to_vec())
290    }
291}
292
293impl Compiler for LLVMCompiler {
294    fn name(&self) -> &str {
295        "llvm"
296    }
297
298    fn get_perfmap_enabled(&self) -> bool {
299        self.config.enable_perfmap
300    }
301
302    fn deterministic_id(&self) -> String {
303        let mut ret = format!(
304            "llvm-{}",
305            match self.config.opt_level {
306                inkwell::OptimizationLevel::None => "opt0",
307                inkwell::OptimizationLevel::Less => "optl",
308                inkwell::OptimizationLevel::Default => "optd",
309                inkwell::OptimizationLevel::Aggressive => "opta",
310            }
311        );
312
313        if self.config.enable_g0m0_opt {
314            ret.push_str("-g0m0");
315        }
316
317        ret
318    }
319
320    /// Get the middlewares for this compiler
321    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>] {
322        &self.config.middlewares
323    }
324
325    fn experimental_native_compile_module(
326        &self,
327        target: &Target,
328        compile_info: &CompileModuleInfo,
329        module_translation: &ModuleTranslationState,
330        // The list of function bodies
331        function_body_inputs: &PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
332        symbol_registry: &dyn SymbolRegistry,
333        // The metadata to inject into the wasmer_metadata section of the object file.
334        wasmer_metadata: &[u8],
335    ) -> Option<Result<Vec<u8>, CompileError>> {
336        Some(self.compile_native_object(
337            target,
338            compile_info,
339            module_translation,
340            function_body_inputs,
341            symbol_registry,
342            wasmer_metadata,
343            self.config.target_binary_format(target),
344        ))
345    }
346
347    /// Compile the module using LLVM, producing a compilation result with
348    /// associated relocations.
349    fn compile_module(
350        &self,
351        target: &Target,
352        compile_info: &CompileModuleInfo,
353        module_translation: &ModuleTranslationState,
354        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
355    ) -> Result<Compilation, CompileError> {
356        //let data = Arc::new(Mutex::new(0));
357
358        let memory_styles = &compile_info.memory_styles;
359        let table_styles = &compile_info.table_styles;
360        let binary_format = self.config.target_binary_format(target);
361
362        let module = &compile_info.module;
363
364        // TODO: merge constants in sections.
365
366        let mut module_custom_sections = PrimaryMap::new();
367
368        let mut eh_frame_section_bytes = vec![];
369        let mut eh_frame_section_relocations = vec![];
370
371        let mut compact_unwind_section_bytes = vec![];
372        let mut compact_unwind_section_relocations = vec![];
373
374        let mut got_targets: HashSet<wasmer_compiler::types::relocation::RelocationTarget> = if matches!(
375            target.triple().binary_format,
376            target_lexicon::BinaryFormat::Macho
377        ) {
378            HashSet::from_iter(vec![RelocationTarget::LibCall(LibCall::EHPersonality)])
379        } else {
380            HashSet::default()
381        };
382
383        let symbol_registry = ModuleBasedSymbolRegistry::new(module.clone());
384
385        let functions = if self.config.num_threads.get() > 1 {
386            let pool = ThreadPoolBuilder::new()
387                .num_threads(self.config.num_threads.get())
388                .build()
389                .map_err(|e| CompileError::Resource(e.to_string()))?;
390            pool.install(|| {
391                function_body_inputs
392                    .iter()
393                    .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
394                    .par_iter()
395                    .map_init(
396                        || {
397                            let target_machine = self.config().target_machine(target);
398                            FuncTranslator::new(target_machine, binary_format).unwrap()
399                        },
400                        |func_translator, (i, input)| {
401                            // TODO: remove (to serialize)
402                            //let _data = data.lock().unwrap();
403
404                            func_translator.translate(
405                                module,
406                                module_translation,
407                                i,
408                                input,
409                                self.config(),
410                                memory_styles,
411                                table_styles,
412                                &symbol_registry,
413                            )
414                        },
415                    )
416                    .collect::<Result<Vec<_>, CompileError>>()
417            })?
418        } else {
419            let target_machine = self.config().target_machine(target);
420            let func_translator = FuncTranslator::new(target_machine, binary_format).unwrap();
421
422            function_body_inputs
423                .iter()
424                .collect::<Vec<(LocalFunctionIndex, &FunctionBodyData<'_>)>>()
425                .into_iter()
426                .map(|(i, input)| {
427                    // TODO: remove (to serialize)
428                    //let _data = data.lock().unwrap();
429
430                    func_translator.translate(
431                        module,
432                        module_translation,
433                        &i,
434                        input,
435                        self.config(),
436                        memory_styles,
437                        table_styles,
438                        &symbol_registry,
439                    )
440                })
441                .collect::<Result<Vec<_>, CompileError>>()?
442        };
443
444        let functions = functions
445            .into_iter()
446            .map(|mut compiled_function| {
447                let first_section = module_custom_sections.len() as u32;
448                for (section_index, custom_section) in compiled_function.custom_sections.iter() {
449                    // TODO: remove this call to clone()
450                    let mut custom_section = custom_section.clone();
451                    for reloc in &mut custom_section.relocations {
452                        if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
453                            reloc.reloc_target = RelocationTarget::CustomSection(
454                                SectionIndex::from_u32(first_section + index.as_u32()),
455                            )
456                        }
457
458                        if reloc.kind.needs_got() {
459                            got_targets.insert(reloc.reloc_target);
460                        }
461                    }
462
463                    if compiled_function
464                        .eh_frame_section_indices
465                        .contains(&section_index)
466                    {
467                        let offset = eh_frame_section_bytes.len() as u32;
468                        for reloc in &mut custom_section.relocations {
469                            reloc.offset += offset;
470                        }
471                        eh_frame_section_bytes.extend_from_slice(custom_section.bytes.as_slice());
472                        eh_frame_section_relocations.extend(custom_section.relocations);
473                        // TODO: we do this to keep the count right, remove it.
474                        module_custom_sections.push(CustomSection {
475                            protection: CustomSectionProtection::Read,
476                            alignment: None,
477                            bytes: SectionBody::new_with_vec(vec![]),
478                            relocations: vec![],
479                        });
480                    } else if compiled_function
481                        .compact_unwind_section_indices
482                        .contains(&section_index)
483                    {
484                        let offset = compact_unwind_section_bytes.len() as u32;
485                        for reloc in &mut custom_section.relocations {
486                            reloc.offset += offset;
487                        }
488                        compact_unwind_section_bytes
489                            .extend_from_slice(custom_section.bytes.as_slice());
490                        compact_unwind_section_relocations.extend(custom_section.relocations);
491                        // TODO: we do this to keep the count right, remove it.
492                        module_custom_sections.push(CustomSection {
493                            protection: CustomSectionProtection::Read,
494                            alignment: None,
495                            bytes: SectionBody::new_with_vec(vec![]),
496                            relocations: vec![],
497                        });
498                    } else {
499                        module_custom_sections.push(custom_section);
500                    }
501                }
502                for reloc in &mut compiled_function.compiled_function.relocations {
503                    if let RelocationTarget::CustomSection(index) = reloc.reloc_target {
504                        reloc.reloc_target = RelocationTarget::CustomSection(
505                            SectionIndex::from_u32(first_section + index.as_u32()),
506                        )
507                    }
508
509                    if reloc.kind.needs_got() {
510                        got_targets.insert(reloc.reloc_target);
511                    }
512                }
513                compiled_function.compiled_function
514            })
515            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
516
517        let mut unwind_info = UnwindInfo::default();
518
519        if !eh_frame_section_bytes.is_empty() {
520            let eh_frame_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
521            // Terminate the eh_frame info with a zero-length CIE.
522            //
523            // There may be more info added later in lib/object/src/module.rs emit_compilation
524            // but that's okay, because an eh_frame can have multiple CIEs.
525            eh_frame_section_bytes.extend_from_slice(&[0, 0, 0, 0]);
526            module_custom_sections.push(CustomSection {
527                protection: CustomSectionProtection::Read,
528                alignment: None,
529                bytes: SectionBody::new_with_vec(eh_frame_section_bytes),
530                relocations: eh_frame_section_relocations,
531            });
532            unwind_info.eh_frame = Some(eh_frame_idx);
533        }
534
535        if !compact_unwind_section_bytes.is_empty() {
536            let cu_index = SectionIndex::from_u32(module_custom_sections.len() as u32);
537            module_custom_sections.push(CustomSection {
538                protection: CustomSectionProtection::Read,
539                alignment: None,
540                bytes: SectionBody::new_with_vec(compact_unwind_section_bytes),
541                relocations: compact_unwind_section_relocations,
542            });
543            unwind_info.compact_unwind = Some(cu_index);
544        }
545
546        let function_call_trampolines = if self.config.num_threads.get() > 1 {
547            let pool = ThreadPoolBuilder::new()
548                .num_threads(self.config.num_threads.get())
549                .build()
550                .map_err(|e| CompileError::Resource(e.to_string()))?;
551            pool.install(|| {
552                module
553                    .signatures
554                    .values()
555                    .collect::<Vec<_>>()
556                    .par_iter()
557                    .map_init(
558                        || {
559                            let target_machine = self.config().target_machine(target);
560                            FuncTrampoline::new(target_machine, binary_format).unwrap()
561                        },
562                        |func_trampoline, sig| {
563                            func_trampoline.trampoline(sig, self.config(), "", compile_info)
564                        },
565                    )
566                    .collect::<Vec<_>>()
567                    .into_iter()
568                    .collect::<Result<PrimaryMap<_, _>, CompileError>>()
569            })?
570        } else {
571            let target_machine = self.config().target_machine(target);
572            let func_trampoline = FuncTrampoline::new(target_machine, binary_format).unwrap();
573            module
574                .signatures
575                .values()
576                .collect::<Vec<_>>()
577                .into_iter()
578                .map(|sig| func_trampoline.trampoline(sig, self.config(), "", compile_info))
579                .collect::<Vec<_>>()
580                .into_iter()
581                .collect::<Result<PrimaryMap<_, _>, CompileError>>()?
582        };
583
584        let dynamic_function_trampolines = if self.config.num_threads.get() > 1 {
585            let pool = ThreadPoolBuilder::new()
586                .num_threads(self.config.num_threads.get())
587                .build()
588                .map_err(|e| CompileError::Resource(e.to_string()))?;
589            pool.install(|| {
590                module
591                    .imported_function_types()
592                    .collect::<Vec<_>>()
593                    .par_iter()
594                    .map_init(
595                        || {
596                            let target_machine = self.config().target_machine(target);
597                            FuncTrampoline::new(target_machine, binary_format).unwrap()
598                        },
599                        |func_trampoline, func_type| {
600                            func_trampoline.dynamic_trampoline(func_type, self.config(), "")
601                        },
602                    )
603                    .collect::<Vec<_>>()
604                    .into_iter()
605                    .collect::<Result<PrimaryMap<_, _>, CompileError>>()
606            })?
607        } else {
608            let target_machine = self.config().target_machine(target);
609            let func_trampoline = FuncTrampoline::new(target_machine, binary_format).unwrap();
610            module
611                .imported_function_types()
612                .collect::<Vec<_>>()
613                .into_iter()
614                .map(|func_type| func_trampoline.dynamic_trampoline(&func_type, self.config(), ""))
615                .collect::<Vec<_>>()
616                .into_iter()
617                .collect::<Result<PrimaryMap<_, _>, CompileError>>()?
618        };
619
620        let mut got = wasmer_compiler::types::function::GOT::empty();
621
622        if !got_targets.is_empty() {
623            let pointer_width = target
624                .triple()
625                .pointer_width()
626                .map_err(|_| CompileError::Codegen("Could not get pointer width".to_string()))?;
627
628            let got_entry_size = match pointer_width {
629                target_lexicon::PointerWidth::U64 => 8,
630                target_lexicon::PointerWidth::U32 => 4,
631                target_lexicon::PointerWidth::U16 => todo!(),
632            };
633
634            let got_entry_reloc_kind = match pointer_width {
635                target_lexicon::PointerWidth::U64 => RelocationKind::Abs8,
636                target_lexicon::PointerWidth::U32 => RelocationKind::Abs4,
637                target_lexicon::PointerWidth::U16 => todo!(),
638            };
639
640            let got_data: Vec<u8> = vec![0; got_targets.len() * got_entry_size];
641            let mut got_relocs = vec![];
642
643            for (i, target) in got_targets.into_iter().enumerate() {
644                got_relocs.push(wasmer_compiler::types::relocation::Relocation {
645                    kind: got_entry_reloc_kind,
646                    reloc_target: target,
647                    offset: (i * got_entry_size) as u32,
648                    addend: 0,
649                });
650            }
651
652            let got_idx = SectionIndex::from_u32(module_custom_sections.len() as u32);
653            module_custom_sections.push(CustomSection {
654                protection: CustomSectionProtection::Read,
655                alignment: None,
656                bytes: SectionBody::new_with_vec(got_data),
657                relocations: got_relocs,
658            });
659            got.index = Some(got_idx);
660        };
661
662        tracing::trace!("Finished compling the module!");
663        Ok(Compilation {
664            functions,
665            custom_sections: module_custom_sections,
666            function_call_trampolines,
667            dynamic_function_trampolines,
668            unwind_info,
669            got,
670        })
671    }
672
673    fn with_opts(
674        &mut self,
675        suggested_compiler_opts: &wasmer_types::target::UserCompilerOptimizations,
676    ) -> Result<(), CompileError> {
677        if suggested_compiler_opts.pass_params.is_some_and(|v| v) {
678            self.config.enable_g0m0_opt = true;
679        }
680        Ok(())
681    }
682}