swamp_code_gen_program/
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 */
5use source_map_cache::SourceMapWrapper;
6use swamp_code_gen::code_bld::CodeBuilderOptions;
7use swamp_code_gen::disasm::{disasm_function, disasm_whole_program};
8use swamp_code_gen::top_state::TopLevelGenState;
9use swamp_compile::Program;
10use swamp_semantic::Function;
11use swamp_vm_types::FrameMemoryAddress;
12use swamp_vm_types::InstructionPositionOffset;
13use swamp_vm_types::types::write_basic_type;
14use time_dilation::ScopedTimer;
15
16pub struct CodeGenOptions {
17    pub show_disasm: bool,
18    pub disasm_filter: Option<String>,
19    pub show_debug: bool,
20    pub show_types: bool,
21    pub ignore_host_call: bool,
22}
23
24/// Function name matching logic similar to test pattern matching
25/// Supports patterns like:
26/// - "`exact_name`" - exact match
27/// - "`prefix::`" - prefix match
28/// - "*suffix" or "prefix*" - wildcard match
29#[must_use]
30pub fn matches_pattern(function_name: &str, pattern: &str) -> bool {
31    if pattern.ends_with("::") {
32        function_name.starts_with(pattern)
33    } else if pattern.contains('*') {
34        let parts: Vec<&str> = pattern.split('*').collect();
35        if parts.len() > 2 {
36            return false;
37        }
38
39        let prefix = parts[0];
40        let suffix = if parts.len() == 2 { parts[1] } else { "" };
41
42        if !function_name.starts_with(prefix) {
43            return false;
44        }
45
46        let remaining_name = &function_name[prefix.len()..];
47        remaining_name.ends_with(suffix)
48    } else {
49        function_name == pattern
50    }
51}
52
53/// Check if a function name matches any of the patterns in the filter string
54#[must_use]
55pub fn function_name_matches_filter(function_name: &str, filter_string: &str) -> bool {
56    if filter_string.trim().is_empty() {
57        return true;
58    }
59
60    let patterns: Vec<&str> = filter_string.split(',').collect();
61
62    for pattern in patterns {
63        let trimmed_pattern = pattern.trim();
64        let simplified_function_name = function_name
65            .strip_prefix("crate::")
66            .map_or(function_name, |stripped| stripped);
67
68        if matches_pattern(simplified_function_name, trimmed_pattern) {
69            return true;
70        }
71    }
72
73    false
74}
75
76/// # Errors
77///
78#[must_use]
79pub fn code_gen_program(
80    program: &Program,
81    source_map_lookup: &SourceMapWrapper,
82    options: &CodeGenOptions,
83) -> TopLevelGenState {
84    let _compile_all_modules_timer = ScopedTimer::new("code generate whole program");
85    let mut code_gen = TopLevelGenState::new(CodeBuilderOptions {
86        should_show_debug: options.show_debug,
87    });
88
89    code_gen.reserve_space_for_constants(&program.state.constants_in_dependency_order);
90
91    /*
92    if let Some(found_main_expression) = &main_module.main_expression {
93        let halt_function = GenOptions {
94            is_halt_function: true,
95        };
96        code_gen.emit_main_function(found_main_expression, &halt_function, source_map_lookup)?;
97    }
98
99     */
100
101    for (_path, module) in program.modules.modules() {
102        for internal_function_def in &module.symbol_table.internal_functions() {
103            code_gen.emit_function_def(
104                internal_function_def,
105                source_map_lookup,
106                options.ignore_host_call,
107            );
108        }
109    }
110
111    for (_associated_on_type, impl_functions) in &program.state.associated_impls.functions {
112        for (_name, func) in &impl_functions.functions {
113            match &**func {
114                Function::Internal(int_fn) => {
115                    code_gen.emit_function_def(int_fn, source_map_lookup, options.ignore_host_call);
116                }
117
118                Function::External(_ext_fn) => {
119                    //
120                }
121
122                Function::Intrinsic(_) => {
123                    //
124                }
125            }
126        }
127    }
128
129    code_gen.emit_constants_expression_functions_in_order(
130        &program.state.constants_in_dependency_order,
131        source_map_lookup,
132    );
133
134    code_gen.finalize();
135
136    if options.show_types {
137        for (id, basic_type) in &code_gen.codegen_state.layout_cache.id_to_layout {
138            let mut str = String::new();
139            write_basic_type(basic_type, FrameMemoryAddress(0), &mut str, 0).expect("output error");
140            eprintln!("{id}>{str}");
141        }
142    }
143
144    if options.show_disasm {
145        if let Some(filter) = &options.disasm_filter {
146            // Filter specific functions
147            let mut current_ip: u32 = 0;
148            let instructions = code_gen.instructions();
149
150            while current_ip < (instructions.len() - 1) as u32 {
151                if let Some(debug_info_for_pc) = code_gen.debug_info().fetch(current_ip as usize) {
152                    let function_name = &debug_info_for_pc.function_debug_info.name;
153                    //                    eprintln!("COMPARE {function_name} vs {filter}");
154
155                    if function_name_matches_filter(function_name, filter) {
156                        // log to stdout since this is a feature "asked" by the user
157                        println!(
158                            "{function_name} ==========================================================================",
159                        );
160                        let end_ip =
161                            current_ip + debug_info_for_pc.function_debug_info.ip_range.count.0;
162                        let instructions_slice =
163                            &instructions[current_ip as usize..end_ip as usize];
164
165                        let output_string = disasm_function(
166                            &debug_info_for_pc.function_debug_info.return_type,
167                            instructions_slice,
168                            &InstructionPositionOffset(current_ip),
169                            code_gen.debug_info(),
170                            source_map_lookup,
171                        );
172                        println!("{output_string}"); // log to stdout since this is a feature "asked" by the user
173                    }
174
175                    current_ip += debug_info_for_pc.function_debug_info.ip_range.count.0;
176                } else {
177                    panic!("instruction pointer that is not covered")
178                }
179            }
180        } else {
181            // Show all functions (existing behavior)
182            disasm_whole_program(
183                code_gen.debug_info(),
184                source_map_lookup,
185                code_gen.instructions(),
186            );
187        }
188    }
189
190    code_gen
191}