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