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