swamp_runtime/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5mod err_wrt;
6pub mod prelude;
7
8use seq_map::SeqMap;
9use source_map_cache::{FileId, KeepTrackOfSourceLine, SourceFileLineInfo, SourceMapLookup};
10use source_map_cache::{SourceMap, SourceMapWrapper};
11use std::env::current_dir;
12use std::fmt::Write as FmtWrite;
13use std::hash::{DefaultHasher, Hash, Hasher};
14use std::path::{Path, PathBuf};
15use swamp_analyzer::Program;
16use swamp_code_gen::{ConstantInfo, GenFunctionInfo};
17use swamp_code_gen_program::{CodeGenOptions, code_gen_program};
18pub use swamp_compile::CompileOptions;
19use swamp_dep_loader::swamp_registry_path;
20use swamp_semantic::{ConstantId, InternalFunctionDefinitionRef, InternalFunctionId};
21use swamp_std::print::print_fn;
22use swamp_types::TypeRef;
23use swamp_vm::host::{HostArgs, HostFunctionCallback};
24use swamp_vm::memory::ExecutionMode;
25use swamp_vm::{TrapCode, Vm, VmSetup, VmState};
26use swamp_vm_debug_info::{DebugInfo, DebugInfoForPc};
27use swamp_vm_disasm::{disasm_color, display_lines};
28use swamp_vm_layout::LayoutCache;
29use swamp_vm_types::types::BasicTypeKind;
30use swamp_vm_types::{BinaryInstruction, InstructionPosition, InstructionRange};
31
32pub struct RunConstantsOptions {
33    pub stderr_adapter: Option<Box<dyn FmtWrite>>,
34}
35
36pub struct RunOptions<'a> {
37    //pub stderr_adapter: Option<Box<dyn FmtWrite>>,
38    pub debug_stats_enabled: bool,
39    pub debug_opcodes_enabled: bool,
40    pub debug_info: &'a DebugInfo,
41    pub source_map_wrapper: SourceMapWrapper<'a>,
42    pub debug_operations_enabled: bool,
43    pub max_count: usize, // dangerous
44    pub use_color: bool,
45    pub debug_memory_enabled: bool,
46}
47
48pub fn run_constants_in_order(
49    vm: &mut Vm,
50    constants_in_order: &SeqMap<ConstantId, ConstantInfo>,
51    host_function_callback: &mut dyn HostFunctionCallback,
52    options: &RunOptions,
53) {
54    vm.memory_mut().set_heap_directly_after_constant_area();
55    for (_key, constant) in constants_in_order {
56        // do not reset heap, all allocations from heap should remain (for now)
57        vm.reset_call_stack();
58
59        if constant.target_constant_memory.ty().is_scalar() {
60        } else {
61            // set memory location into to r0
62            vm.registers[0] = constant.target_constant_memory.addr().0;
63        }
64
65        run_function_with_debug(vm, &constant.ip_range, host_function_callback, options);
66
67        if constant.target_constant_memory.ty().is_scalar() {
68            match constant.target_constant_memory.ty().kind {
69                BasicTypeKind::S32 | BasicTypeKind::Fixed32 | BasicTypeKind::U32 => {
70                    let heap_ptr = vm
71                        .memory_mut()
72                        .get_heap_ptr(constant.target_constant_memory.addr().0 as usize)
73                        .cast::<u32>();
74                    unsafe {
75                        *heap_ptr = vm.registers[0];
76                    }
77                }
78                BasicTypeKind::B8 | BasicTypeKind::U8 => {
79                    let heap_ptr = vm
80                        .memory_mut()
81                        .get_heap_ptr(constant.target_constant_memory.addr().0 as usize)
82                        .cast::<u8>();
83                    unsafe {
84                        *heap_ptr = vm.registers[0] as u8;
85                    }
86                }
87                _ => todo!(),
88            }
89        }
90
91        let return_layout = constant.target_constant_memory.ty();
92
93        assert_eq!(
94            constant.target_constant_memory.size(),
95            return_layout.total_size
96        );
97
98        // TODO: Bring this back
99        /*
100        if let Some(x) = &mut options.stderr_adapter {
101            swamp_vm_pretty_print::print_value(
102                x.as_mut(),
103                vm.frame_memory(),
104                vm.memory(),
105                StackMemoryAddress(0),
106                return_layout,
107                &constant.constant_ref.assigned_name,
108            )
109                .unwrap();
110        }
111
112         */
113    }
114
115    // We need to properly preserve string headers when incorporating heap into constant area
116    vm.memory_mut().incorporate_heap_into_constant_area();
117}
118
119// "/Users/peter/external/swamp_autobattler/scripts"
120#[must_use]
121pub fn crate_and_registry(path_to_swamp: &Path) -> SourceMap {
122    let mut mounts = SeqMap::new();
123    //  let path_buf = Path::new(path_to_swamp).to_path_buf();
124    mounts
125        .insert("crate".to_string(), path_to_swamp.to_path_buf())
126        .unwrap();
127
128    if let Some(found_swamp_home) = swamp_registry_path() {
129        mounts
130            .insert("registry".to_string(), found_swamp_home)
131            .unwrap();
132    }
133
134    SourceMap::new(&mounts).expect("source map failed")
135}
136
137pub struct CompileAndMaybeCodeGenResult {
138    pub compile: CompileResult,
139    pub codegen: Option<CodeGenResult>,
140}
141
142pub struct CompileResult {
143    pub program: Program,
144}
145
146pub struct CodeGenResult {
147    pub instructions: Vec<BinaryInstruction>,
148    pub constants_in_order: SeqMap<ConstantId, ConstantInfo>,
149    pub functions: SeqMap<InternalFunctionId, GenFunctionInfo>,
150    pub prepared_constant_memory: Vec<u8>,
151    pub layout_cache: LayoutCache,
152    pub debug_info: DebugInfo,
153}
154
155impl CodeGenResult {
156    #[must_use]
157    pub fn find_function(&self, formal_name: &str) -> Option<&GenFunctionInfo> {
158        self.functions
159            .values()
160            .find(|&func| func.internal_function_definition.assigned_name == formal_name)
161    }
162}
163
164pub fn compile_main_path(
165    source_map: &mut SourceMap,
166    root_module_path: &[String],
167    options: &CompileOptions,
168) -> Option<CompileResult> {
169    let program =
170        swamp_compile::bootstrap_and_compile(source_map, root_module_path, options).ok()?;
171
172    Some(CompileResult { program })
173}
174
175#[must_use]
176pub fn code_gen(
177    program: &Program,
178    source_map_wrapper: &SourceMapWrapper,
179    code_gen_options: &CodeGenOptions,
180) -> CodeGenResult {
181    let top_gen_state = code_gen_program(program, source_map_wrapper, code_gen_options);
182
183    let all_types = top_gen_state.codegen_state.layout_cache.clone();
184    let (instructions, constants_in_order, emit_function_infos, constant_memory, debug_info) =
185        top_gen_state.take_instructions_and_constants();
186
187    CodeGenResult {
188        debug_info,
189        instructions,
190        constants_in_order,
191        functions: emit_function_infos,
192        prepared_constant_memory: constant_memory,
193        layout_cache: all_types,
194    }
195}
196
197#[must_use]
198pub fn create_vm_with_standard_settings(
199    instructions: &[BinaryInstruction],
200    prepared_constant_memory: &[u8],
201) -> Vm {
202    let vm_setup = VmSetup {
203        stack_memory_size: 800 * 1024 * 1024, // 512 MiB
204        heap_memory_size: 128 * 1024 * 1024,  // 128 MiB for transient heap allocation
205        constant_memory: prepared_constant_memory.to_vec(),
206        debug_opcodes_enabled: false,
207        debug_stats_enabled: false,
208        debug_operations_enabled: false,
209    };
210
211    Vm::new(instructions.to_vec(), vm_setup)
212}
213
214pub fn run_first_time(
215    vm: &mut Vm,
216    constants_in_order: &SeqMap<ConstantId, ConstantInfo>,
217    host_function_callback: &mut dyn HostFunctionCallback,
218    options: &RunOptions,
219) {
220    run_constants_in_order(vm, constants_in_order, host_function_callback, options);
221}
222
223pub fn run_as_fast_as_possible(
224    vm: &mut Vm,
225    function_to_run: &GenFunctionInfo,
226    host_function_callback: &mut dyn HostFunctionCallback,
227    run_options: RunOptions,
228) {
229    vm.reset_stack_and_heap_to_constant_limit();
230    vm.state = VmState::Normal;
231    vm.debug_opcodes_enabled = false;
232    vm.debug_operations_enabled = run_options.debug_operations_enabled;
233    vm.debug_stats_enabled = run_options.debug_stats_enabled;
234
235    vm.execute_from_ip(&function_to_run.ip_range.start, host_function_callback);
236    if matches!(vm.state, VmState::Trap(_) | VmState::Panic(_)) {
237        show_crash_info(vm, run_options.debug_info, &run_options.source_map_wrapper);
238    }
239}
240
241fn calculate_memory_checksum(memory: &[u8]) -> u64 {
242    let mut hasher = DefaultHasher::new();
243    memory.hash(&mut hasher);
244    hasher.finish()
245}
246
247pub fn run_function(
248    vm: &mut Vm,
249    ip_range: &InstructionRange,
250    host_function_callback: &mut dyn HostFunctionCallback,
251    run_options: &RunOptions,
252) {
253    vm.reset_stack_and_heap_to_constant_limit();
254    //vm.reset_debug();
255    vm.state = VmState::Normal;
256    // vm.debug_opcodes_enabled = run_options.debug_opcodes_enabled;
257    //vm.debug_operations_enabled = run_options.debug_operations_enabled;
258    //vm.debug_stats_enabled = run_options.debug_stats_enabled;
259    vm.execute_from_ip(&ip_range.start, host_function_callback);
260
261    if matches!(vm.state, VmState::Trap(_) | VmState::Panic(_)) {
262        show_crash_info(vm, run_options.debug_info, &run_options.source_map_wrapper);
263    }
264}
265
266pub fn show_call_stack(
267    vm: &Vm,
268    debug_info: &DebugInfo,
269    info: &DebugInfoForPc,
270    source_map_wrapper: &SourceMapWrapper,
271) {
272    // Show call stack
273    eprintln!("\nšŸ“ž Call stack:");
274
275    // Current function (where the crash occurred)
276    if info.meta.node.span.file_id != 0 {
277        let (line, _column) = source_map_wrapper.source_map.get_span_location_utf8(
278            info.meta.node.span.file_id,
279            info.meta.node.span.offset as usize,
280        );
281        let relative_path = source_map_wrapper
282            .source_map
283            .fetch_relative_filename(info.meta.node.span.file_id);
284
285        // Get the actual source line
286        if let Some(source_line) =
287            source_map_wrapper.get_source_line(info.meta.node.span.file_id as FileId, line)
288        {
289            eprintln!(
290                "  {}: {} {}",
291                tinter::bright_cyan("0"),
292                tinter::blue(&info.function_debug_info.name),
293                tinter::bright_black("(current)")
294            );
295            eprintln!(
296                "{:4} {} {}",
297                tinter::bright_black(&line.to_string()),
298                tinter::green("|"),
299                source_line.trim()
300            );
301            eprintln!(
302                "{} {}",
303                tinter::bright_black("->"),
304                tinter::cyan(&format!("{relative_path}:{line}"))
305            );
306        } else {
307            eprintln!(
308                "  {}: {} {}",
309                tinter::bright_cyan("0"),
310                tinter::blue(&info.function_debug_info.name),
311                tinter::bright_black("(current)")
312            );
313            eprintln!(
314                "{} {}",
315                tinter::bright_black("->"),
316                tinter::cyan(&format!("{relative_path}:{line}"))
317            );
318        }
319    } else {
320        eprintln!(
321            "  {}: {} {}",
322            tinter::bright_cyan("0"),
323            tinter::blue(&info.function_debug_info.name),
324            tinter::bright_black("(current)")
325        );
326    }
327
328    let call_stack = vm.call_stack();
329    for (i, frame) in call_stack.iter().enumerate().rev() {
330        let frame_pc = if frame.return_address > 0 {
331            frame.return_address - 1
332        } else {
333            frame.return_address
334        };
335
336        if let Some(frame_info) = debug_info.fetch(frame_pc) {
337            if frame_info.meta.node.span.file_id != 0 {
338                let (line, _column) = source_map_wrapper.source_map.get_span_location_utf8(
339                    frame_info.meta.node.span.file_id,
340                    frame_info.meta.node.span.offset as usize,
341                );
342                let relative_path = source_map_wrapper
343                    .source_map
344                    .fetch_relative_filename(frame_info.meta.node.span.file_id);
345
346                if let Some(source_line) = source_map_wrapper
347                    .get_source_line(frame_info.meta.node.span.file_id as FileId, line)
348                {
349                    eprintln!(
350                        "  {}: {}",
351                        tinter::bright_cyan(&(i + 1).to_string()),
352                        tinter::blue(&frame_info.function_debug_info.name)
353                    );
354                    eprintln!(
355                        "{:4} {} {}",
356                        tinter::bright_black(&line.to_string()),
357                        tinter::green("|"),
358                        source_line.trim()
359                    );
360                    eprintln!(
361                        "{} {}",
362                        tinter::bright_black("->"),
363                        tinter::cyan(&format!("{relative_path}:{line}"))
364                    );
365                } else {
366                    eprintln!(
367                        "  {}: {}",
368                        tinter::bright_cyan(&(i + 1).to_string()),
369                        tinter::blue(&frame_info.function_debug_info.name)
370                    );
371                    eprintln!(
372                        "{} {}",
373                        tinter::bright_black("->"),
374                        tinter::cyan(&format!("{relative_path}:{line}"))
375                    );
376                }
377            } else {
378                eprintln!(
379                    "  {}: {}",
380                    tinter::bright_cyan(&(i + 1).to_string()),
381                    tinter::blue(&frame_info.function_debug_info.name)
382                );
383            }
384        } else {
385            eprintln!(
386                "  {}: {} {}",
387                tinter::bright_cyan(&(i + 1).to_string()),
388                tinter::red("<unknown function>"),
389                tinter::bright_black(&format!("(pc: {frame_pc:04X})"))
390            );
391        }
392    }
393}
394
395pub fn show_crash_info(vm: &Vm, debug_info: &DebugInfo, source_map_wrapper: &SourceMapWrapper) {
396    let pc = vm.pc();
397
398    // PC has advanced past the instruction that caused the trap/panic, so look at pc - 1
399
400    let trap_pc = if pc > 0 { pc - 1 } else { pc };
401
402    if let Some(info) = debug_info.fetch(trap_pc) {
403        match &vm.state {
404            VmState::Trap(trap_code) => {
405                eprintln!("\n🚫 VM TRAP: {trap_code}");
406            }
407            VmState::Panic(message) => {
408                eprintln!("\nšŸ’„ VM PANIC: {message}");
409            }
410            _ => unreachable!(),
411        }
412
413        if info.meta.node.span.file_id != 0 {
414            let (line, column) = source_map_wrapper.source_map.get_span_location_utf8(
415                info.meta.node.span.file_id,
416                info.meta.node.span.offset as usize,
417            );
418
419            eprintln!("šŸ“ Source location:");
420            let mut string = String::new();
421            display_lines(
422                &mut string,
423                info.meta.node.span.file_id as FileId,
424                line.saturating_sub(2), // Show a couple lines before
425                line + 2,               // Show a couple lines after
426                source_map_wrapper,
427            );
428            eprint!("{string}");
429
430            let relative_path = source_map_wrapper
431                .source_map
432                .fetch_relative_filename(info.meta.node.span.file_id);
433            eprintln!("   at {relative_path}:{line}:{column}");
434        }
435
436        // Also show the instruction that caused the trap/panic
437        let instruction = &vm.instructions()[trap_pc];
438        let disassembler_string = disasm_color(
439            instruction,
440            &info.function_debug_info.frame_memory,
441            &info.meta,
442            &InstructionPosition(trap_pc as u32),
443        );
444        eprintln!("šŸ’» Failing instruction: {trap_pc:04X}> {disassembler_string}");
445
446        show_call_stack(vm, debug_info, &info, source_map_wrapper);
447    }
448}
449
450pub fn run_function_with_debug(
451    vm: &mut Vm,
452    function_to_run: &InstructionRange,
453    host_function_callback: &mut dyn HostFunctionCallback,
454    run_options: &RunOptions,
455) {
456    vm.reset_stack_and_heap_to_constant_limit();
457    //vm.reset_debug();
458    vm.state = VmState::Normal;
459    vm.debug_opcodes_enabled = run_options.debug_opcodes_enabled;
460    vm.debug_operations_enabled = run_options.debug_operations_enabled;
461    vm.debug_stats_enabled = run_options.debug_stats_enabled;
462    vm.set_pc(&function_to_run.start);
463
464    let mut debug_count = 0;
465
466    let use_color = run_options.use_color;
467
468    let mut last_line_info = KeepTrackOfSourceLine::new();
469
470    let saved_fp = vm.memory().constant_memory_size;
471    let hash_before: u64 = if run_options.debug_memory_enabled {
472        calculate_memory_checksum(vm.all_memory_up_to(saved_fp))
473    } else {
474        0
475    };
476
477    while !vm.is_execution_complete() {
478        debug_count += 1;
479
480        if run_options.max_count != 0 && (debug_count >= run_options.max_count) {
481            if vm.state == VmState::Normal {
482                vm.internal_trap(TrapCode::StoppedByTestHarness);
483            }
484            break;
485        }
486
487        let pc = vm.pc();
488        #[cfg(feature = "debug_vm")]
489        if run_options.debug_opcodes_enabled {
490            let regs = [
491                0, 1, 2, 3, 4, 5, 6, 7, 8, 128, 129, 130, 131, 132, 133, 134, 135,
492            ];
493            if use_color {
494                print!(
495                    "{}",
496                    tinter::bright_black(&format!("fp:{:08X}, sp:{:08X} ", vm.fp(), vm.sp(),))
497                );
498
499                for reg in regs {
500                    let reg_name = &format!("r{reg}");
501                    print!(
502                        "{}",
503                        tinter::bright_black(&format!("{reg_name:>3}:{:08X}, ", vm.registers[reg]))
504                    );
505                }
506                println!();
507            } else {
508                // TODO!: Use style instead
509                print!("{}", &format!("fp:{:08X}, sp:{:08X}, ", vm.fp(), vm.sp()));
510
511                for reg in regs {
512                    let reg_name = &format!("r{reg}");
513                    print!("{}", &format!("{reg_name:>3}:{:08X}, ", vm.registers[reg]));
514                }
515                println!();
516            }
517
518            let info = run_options.debug_info.fetch(pc).unwrap();
519
520            if info.meta.node.span.file_id != 0 {
521                let (line, column) = run_options
522                    .source_map_wrapper
523                    .source_map
524                    .get_span_location_utf8(
525                        info.meta.node.span.file_id,
526                        info.meta.node.span.offset as usize,
527                    );
528                let source_line_info = SourceFileLineInfo {
529                    row: line,
530                    file_id: info.meta.node.span.file_id as usize,
531                };
532
533                if let Some((start_row, end_row)) =
534                    last_line_info.check_if_new_line(&source_line_info)
535                {
536                    let mut string = String::new();
537                    display_lines(
538                        &mut string,
539                        source_line_info.file_id as FileId,
540                        start_row,
541                        end_row,
542                        &run_options.source_map_wrapper,
543                    );
544                    print!("{string}");
545                }
546            }
547
548            let instruction = &vm.instructions()[pc];
549            let string = disasm_color(
550                instruction,
551                &info.function_debug_info.frame_memory,
552                &info.meta,
553                &InstructionPosition(pc as u32),
554            );
555            println!("{pc:04X}> {string}");
556        }
557
558        //        eprintln!("constant (from address 0x34): {:?}", &vm.heap_memory()[0x34..0x34+32]);
559
560        vm.step(host_function_callback);
561
562        // Check if VM encountered a trap or panic and show source information
563        if matches!(vm.state, VmState::Trap(_) | VmState::Panic(_)) {
564            show_crash_info(vm, run_options.debug_info, &run_options.source_map_wrapper);
565        }
566
567        if run_options.debug_memory_enabled
568            && vm.memory().execution_mode != ExecutionMode::ConstantEvaluation
569        {
570            // During constant execution, of course the constant memory changes
571            let hash_after = calculate_memory_checksum(vm.all_memory_up_to(saved_fp));
572            //vm.state = VmState::Trap(TrapCode::VecBoundsFail);
573            assert!(
574                (hash_after == hash_before),
575                "INTERNAL ERROR: constant memory has been written to"
576            );
577            /*
578            assert_eq!(
579                hash_before, hash_after,
580                "constant memory has been clobbered"
581            );
582
583             */
584        }
585    }
586}
587
588pub struct CompileAndCodeGenOptions {
589    pub compile_options: CompileOptions,
590    pub code_gen_options: CodeGenOptions,
591    pub skip_codegen: bool,
592}
593
594#[must_use]
595pub fn compile_and_code_gen(
596    path_to_root_of_swamp_files: &Path,
597    main_module_path: &[String],
598    options: &CompileAndCodeGenOptions,
599) -> Option<(CompileAndMaybeCodeGenResult, SourceMap)> {
600    let mut source_map = crate_and_registry(path_to_root_of_swamp_files);
601    let current_dir = PathBuf::from(Path::new(""));
602
603    let compile_result =
604        compile_main_path(&mut source_map, main_module_path, &options.compile_options)?;
605
606    let source_map_wrapper = SourceMapWrapper {
607        source_map: &source_map,
608        current_dir,
609    };
610
611    let maybe_code_gen_result =
612        if options.skip_codegen || !compile_result.program.state.errors().is_empty() {
613            None
614        } else {
615            let code_gen_result = code_gen(
616                &compile_result.program,
617                &source_map_wrapper,
618                &options.code_gen_options,
619            );
620            Some(code_gen_result)
621        };
622
623    Some((
624        CompileAndMaybeCodeGenResult {
625            compile: compile_result,
626            codegen: maybe_code_gen_result,
627        },
628        source_map,
629    ))
630}
631
632pub struct CompileCodeGenVmResult {
633    pub compile: CompileResult,
634    pub codegen: CodeGenAndVmResult,
635}
636
637pub enum CompileAndVmResult {
638    CompileOnly(CompileResult),
639    CompileAndVm(CompileCodeGenVmResult),
640}
641
642pub struct CodeGenAndVmResult {
643    pub vm: Vm,
644    pub code_gen_result: CodeGenResult,
645    pub source_map: SourceMap,
646}
647
648pub struct StandardOnlyHostCallbacks {}
649
650impl HostFunctionCallback for StandardOnlyHostCallbacks {
651    fn dispatch_host_call(&mut self, args: HostArgs) {
652        match args.function_id {
653            1 => print_fn(args),
654
655            _ => panic!("unknown external {}", args.function_id),
656        }
657    }
658}
659
660impl CompileCodeGenVmResult {
661    #[must_use]
662    pub fn get_internal_member_function(
663        &self,
664        ty: &TypeRef,
665        member_function_str: &str,
666    ) -> Option<&InternalFunctionDefinitionRef> {
667        self.compile
668            .program
669            .state
670            .associated_impls
671            .get_internal_member_function(ty, member_function_str)
672    }
673    #[must_use]
674    pub fn get_gen_internal_member_function(
675        &self,
676        ty: &TypeRef,
677        member_function_str: &str,
678    ) -> Option<&GenFunctionInfo> {
679        let x = self
680            .get_internal_member_function(ty, member_function_str)
681            .unwrap();
682
683        self.codegen
684            .code_gen_result
685            .functions
686            .get(&x.program_unique_id)
687    }
688}
689
690#[must_use]
691pub fn compile_codegen_and_create_vm_and_run_first_time(
692    root_directory: &Path,
693    root_module: &[String],
694    compile_and_code_gen_options: CompileAndCodeGenOptions,
695) -> Option<CompileAndVmResult> {
696    let result =
697        compile_codegen_and_create_vm(root_directory, root_module, &compile_and_code_gen_options);
698    if let Some(mut first_result) = result {
699        if let CompileAndVmResult::CompileAndVm(found_result) = &mut first_result {
700            let mut callbacks = StandardOnlyHostCallbacks {};
701            let run_first_options = RunOptions {
702                debug_stats_enabled: false,
703                debug_opcodes_enabled: false,
704                debug_operations_enabled: false,
705                use_color: true,
706                max_count: 0,
707                debug_info: &found_result.codegen.code_gen_result.debug_info,
708                source_map_wrapper: SourceMapWrapper {
709                    source_map: &found_result.codegen.source_map,
710                    current_dir: current_dir().unwrap(),
711                },
712                debug_memory_enabled: false,
713            };
714
715            run_first_time(
716                &mut found_result.codegen.vm,
717                &found_result.codegen.code_gen_result.constants_in_order,
718                &mut callbacks,
719                &run_first_options,
720            );
721            Some(first_result)
722        } else {
723            None
724        }
725    } else {
726        None
727    }
728}
729
730/// The root module is needed so it knows which mod that should be considered.
731#[must_use]
732pub fn compile_codegen_and_create_vm(
733    root_directory: &Path,
734    root_module: &[String],
735    compile_and_code_gen_options: &CompileAndCodeGenOptions,
736) -> Option<CompileAndVmResult> {
737    let (compile_and_maybe_code_gen, source_map) =
738        compile_and_code_gen(root_directory, root_module, compile_and_code_gen_options)?;
739
740    if let Some(code_gen_result) = compile_and_maybe_code_gen.codegen {
741        let vm = create_vm_with_standard_settings(
742            &code_gen_result.instructions,
743            &code_gen_result.prepared_constant_memory,
744        );
745
746        let code_gen_and_vm = CodeGenAndVmResult {
747            vm,
748            code_gen_result,
749            source_map,
750        };
751
752        Some(CompileAndVmResult::CompileAndVm(CompileCodeGenVmResult {
753            compile: compile_and_maybe_code_gen.compile,
754            codegen: code_gen_and_vm,
755        }))
756    } else {
757        Some(CompileAndVmResult::CompileOnly(
758            compile_and_maybe_code_gen.compile,
759        ))
760    }
761}