Skip to main content

seqc/codegen/
program.rs

1//! Program Code Generation
2//!
3//! This module contains the main entry points for generating LLVM IR
4//! from a complete Seq program.
5
6use super::{
7    CodeGen, CodeGenError, emit_runtime_decls, ffi_c_args, ffi_return_type, get_target_triple,
8};
9use crate::ast::Program;
10use crate::config::CompilerConfig;
11use crate::ffi::FfiBindings;
12use crate::types::Type;
13use std::collections::HashMap;
14use std::fmt::Write as _;
15
16impl CodeGen {
17    /// Generate LLVM IR for entire program
18    pub fn codegen_program(
19        &mut self,
20        program: &Program,
21        type_map: HashMap<usize, Type>,
22        statement_types: HashMap<(String, usize), Type>,
23    ) -> Result<String, CodeGenError> {
24        self.codegen_program_with_config(
25            program,
26            type_map,
27            statement_types,
28            &CompilerConfig::default(),
29        )
30    }
31
32    /// Generate LLVM IR for entire program with custom configuration
33    ///
34    /// This allows external projects to extend the compiler with additional
35    /// builtins that will be declared and callable from Seq code.
36    pub fn codegen_program_with_config(
37        &mut self,
38        program: &Program,
39        type_map: HashMap<usize, Type>,
40        statement_types: HashMap<(String, usize), Type>,
41        config: &CompilerConfig,
42    ) -> Result<String, CodeGenError> {
43        // Store type map for use during code generation
44        self.type_map = type_map;
45        self.statement_types = statement_types;
46
47        // Store union definitions for pattern matching
48        self.unions = program.unions.clone();
49
50        // Build external builtins map from config
51        self.external_builtins = config
52            .external_builtins
53            .iter()
54            .map(|b| (b.seq_name.clone(), b.symbol.clone()))
55            .collect();
56
57        // Flow instrumentation config
58        self.instrument = config.instrument;
59        if self.instrument {
60            for (id, word) in program.words.iter().enumerate() {
61                self.word_instrument_ids.insert(word.name.clone(), id);
62            }
63        }
64
65        // Verify we have a main word
66        if program.find_word("main").is_none() {
67            return Err(CodeGenError::Logic("No main word defined".to_string()));
68        }
69
70        // Generate all user-defined words
71        for word in &program.words {
72            self.codegen_word(word)?;
73        }
74
75        // Generate main function
76        self.codegen_main()?;
77
78        // Assemble final IR
79        let mut ir = String::new();
80
81        // Target and type declarations
82        writeln!(&mut ir, "; ModuleID = 'main'")?;
83        writeln!(&mut ir, "target triple = \"{}\"", get_target_triple())?;
84        writeln!(&mut ir)?;
85
86        // Value type definition (8-byte tagged pointer)
87        self.emit_value_type_def(&mut ir)?;
88
89        // String and symbol constants
90        self.emit_string_and_symbol_globals(&mut ir)?;
91
92        // Instrumentation globals (when --instrument)
93        if self.instrument {
94            self.emit_instrumentation_globals(&mut ir)?;
95        }
96
97        // Runtime function declarations
98        emit_runtime_decls(&mut ir)?;
99
100        // External builtin declarations (from config)
101        if !self.external_builtins.is_empty() {
102            writeln!(&mut ir, "; External builtin declarations")?;
103            for symbol in self.external_builtins.values() {
104                // All external builtins follow the standard stack convention: ptr -> ptr
105                writeln!(&mut ir, "declare ptr @{}(ptr)", symbol)?;
106            }
107            writeln!(&mut ir)?;
108        }
109
110        // Quotation functions (generated from quotation literals)
111        if !self.quotation_functions.is_empty() {
112            writeln!(&mut ir, "; Quotation functions")?;
113            ir.push_str(&self.quotation_functions);
114            writeln!(&mut ir)?;
115        }
116
117        // User-defined words and main
118        ir.push_str(&self.output);
119
120        Ok(ir)
121    }
122
123    /// Generate LLVM IR for entire program with FFI support
124    ///
125    /// This is the main entry point for compiling programs that use FFI.
126    pub fn codegen_program_with_ffi(
127        &mut self,
128        program: &Program,
129        type_map: HashMap<usize, Type>,
130        statement_types: HashMap<(String, usize), Type>,
131        config: &CompilerConfig,
132        ffi_bindings: &FfiBindings,
133    ) -> Result<String, CodeGenError> {
134        // Store FFI bindings
135        self.ffi_bindings = ffi_bindings.clone();
136
137        // Generate FFI wrapper functions
138        self.generate_ffi_wrappers()?;
139
140        // Store type map for use during code generation
141        self.type_map = type_map;
142        self.statement_types = statement_types;
143
144        // Store union definitions for pattern matching
145        self.unions = program.unions.clone();
146
147        // Build external builtins map from config
148        self.external_builtins = config
149            .external_builtins
150            .iter()
151            .map(|b| (b.seq_name.clone(), b.symbol.clone()))
152            .collect();
153
154        // Flow instrumentation config
155        self.instrument = config.instrument;
156        if self.instrument {
157            for (id, word) in program.words.iter().enumerate() {
158                self.word_instrument_ids.insert(word.name.clone(), id);
159            }
160        }
161
162        // Verify we have a main word
163        if program.find_word("main").is_none() {
164            return Err(CodeGenError::Logic("No main word defined".to_string()));
165        }
166
167        // Generate all user-defined words
168        for word in &program.words {
169            self.codegen_word(word)?;
170        }
171
172        // Generate main function
173        self.codegen_main()?;
174
175        // Assemble final IR
176        let mut ir = String::new();
177
178        // Target and type declarations
179        writeln!(&mut ir, "; ModuleID = 'main'")?;
180        writeln!(&mut ir, "target triple = \"{}\"", get_target_triple())?;
181        writeln!(&mut ir)?;
182
183        // Value type definition (8-byte tagged pointer)
184        self.emit_value_type_def(&mut ir)?;
185
186        // String and symbol constants
187        self.emit_string_and_symbol_globals(&mut ir)?;
188
189        // Instrumentation globals (when --instrument)
190        if self.instrument {
191            self.emit_instrumentation_globals(&mut ir)?;
192        }
193
194        // Runtime function declarations (same as codegen_program_with_config)
195        self.emit_runtime_declarations(&mut ir)?;
196
197        // FFI C function declarations
198        if !self.ffi_bindings.functions.is_empty() {
199            writeln!(&mut ir, "; FFI C function declarations")?;
200            writeln!(&mut ir, "declare ptr @malloc(i64)")?;
201            writeln!(&mut ir, "declare void @free(ptr)")?;
202            writeln!(&mut ir, "declare i64 @strlen(ptr)")?;
203            writeln!(&mut ir, "declare ptr @memcpy(ptr, ptr, i64)")?;
204            // Declare FFI string helpers from runtime
205            writeln!(
206                &mut ir,
207                "declare ptr @patch_seq_string_to_cstring(ptr, ptr)"
208            )?;
209            writeln!(
210                &mut ir,
211                "declare ptr @patch_seq_cstring_to_string(ptr, ptr)"
212            )?;
213            for func in self.ffi_bindings.functions.values() {
214                let c_ret_type = ffi_return_type(&func.return_spec);
215                let c_args = ffi_c_args(&func.args);
216                writeln!(
217                    &mut ir,
218                    "declare {} @{}({})",
219                    c_ret_type, func.c_name, c_args
220                )?;
221            }
222            writeln!(&mut ir)?;
223        }
224
225        // External builtin declarations (from config)
226        if !self.external_builtins.is_empty() {
227            writeln!(&mut ir, "; External builtin declarations")?;
228            for symbol in self.external_builtins.values() {
229                writeln!(&mut ir, "declare ptr @{}(ptr)", symbol)?;
230            }
231            writeln!(&mut ir)?;
232        }
233
234        // FFI wrapper functions
235        if !self.ffi_wrapper_code.is_empty() {
236            writeln!(&mut ir, "; FFI wrapper functions")?;
237            ir.push_str(&self.ffi_wrapper_code);
238            writeln!(&mut ir)?;
239        }
240
241        // Quotation functions
242        if !self.quotation_functions.is_empty() {
243            writeln!(&mut ir, "; Quotation functions")?;
244            ir.push_str(&self.quotation_functions);
245            writeln!(&mut ir)?;
246        }
247
248        // User-defined words and main
249        ir.push_str(&self.output);
250
251        Ok(ir)
252    }
253
254    /// Emit runtime function declarations
255    pub(super) fn emit_runtime_declarations(&self, ir: &mut String) -> Result<(), CodeGenError> {
256        emit_runtime_decls(ir)
257    }
258
259    /// Emit instrumentation globals for --instrument mode
260    ///
261    /// Generates:
262    /// - @seq_word_counters: array of i64 counters (one per word)
263    /// - @seq_word_name_K: per-word C string constants
264    /// - @seq_word_names: array of pointers to name strings
265    fn emit_instrumentation_globals(&self, ir: &mut String) -> Result<(), CodeGenError> {
266        let n = self.word_instrument_ids.len();
267        if n == 0 {
268            return Ok(());
269        }
270
271        writeln!(ir, "; Instrumentation globals (--instrument)")?;
272
273        // Counter array: [N x i64] zeroinitializer
274        writeln!(
275            ir,
276            "@seq_word_counters = global [{} x i64] zeroinitializer",
277            n
278        )?;
279
280        // Build sorted list of (id, name) for deterministic output
281        let mut words: Vec<(usize, &str)> = self
282            .word_instrument_ids
283            .iter()
284            .map(|(name, &id)| (id, name.as_str()))
285            .collect();
286        words.sort_by_key(|&(id, _)| id);
287
288        // Per-word name string constants
289        for &(id, name) in &words {
290            let name_bytes = name.as_bytes();
291            let len = name_bytes.len() + 1; // +1 for null terminator
292            let escaped: String = name_bytes
293                .iter()
294                .map(|&b| format!("\\{:02X}", b))
295                .collect::<String>();
296            writeln!(
297                ir,
298                "@seq_word_name_{} = private constant [{} x i8] c\"{}\\00\"",
299                id, len, escaped
300            )?;
301        }
302
303        // Name pointer table
304        let ptrs: Vec<String> = words
305            .iter()
306            .map(|&(id, _name)| format!("ptr @seq_word_name_{}", id))
307            .collect();
308        writeln!(
309            ir,
310            "@seq_word_names = private constant [{} x ptr] [{}]",
311            n,
312            ptrs.join(", ")
313        )?;
314
315        writeln!(ir)?;
316        Ok(())
317    }
318}