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