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