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