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