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::{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 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, 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 vm.reset_call_stack();
58
59 if constant.target_constant_memory.ty().is_scalar() {} else {
60 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 }
113
114 vm.memory_mut().incorporate_heap_into_constant_area();
116}
117
118#[must_use]
120pub fn crate_and_registry(path_to_swamp: &Path, run_mode: &RunMode) -> SourceMap {
121 let mut mounts = SeqMap::new();
122 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, heap_memory_size: 512 * 1024, 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 eprintln!("\nš Call stack:");
271
272 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 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 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), line + 2, 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 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.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 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 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 let hash_after = calculate_memory_checksum(vm.all_memory_up_to(saved_fp));
567 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#[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}