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::{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 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() {} else {
62 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 }
115
116 vm.memory_mut().incorporate_heap_into_constant_area();
118}
119
120#[must_use]
122pub fn crate_and_registry(path_to_swamp: &Path, run_mode: &RunMode) -> SourceMap {
123 let mut mounts = SeqMap::new();
124 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, heap_memory_size: 512 * 1024, 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 eprintln!("\nš Call stack:");
296
297 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 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 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), line + 2, 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 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 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 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 let hash_after = calculate_memory_checksum(vm.all_memory_up_to(saved_fp));
606 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#[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}