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        // Verify we have a main word
58        if program.find_word("main").is_none() {
59            return Err(CodeGenError::Logic("No main word defined".to_string()));
60        }
61
62        // Generate all user-defined words
63        for word in &program.words {
64            self.codegen_word(word)?;
65        }
66
67        // Generate main function
68        self.codegen_main()?;
69
70        // Assemble final IR
71        let mut ir = String::new();
72
73        // Target and type declarations
74        writeln!(&mut ir, "; ModuleID = 'main'")?;
75        writeln!(&mut ir, "target triple = \"{}\"", get_target_triple())?;
76        writeln!(&mut ir)?;
77
78        // Value type definition
79        #[cfg(not(feature = "nanbox"))]
80        {
81            // Non-nanbox: Rust enum with #[repr(C)], 40 bytes: discriminant + largest variant payload
82            writeln!(&mut ir, "; Value type (Rust enum - 40 bytes)")?;
83            writeln!(&mut ir, "%Value = type {{ i64, i64, i64, i64, i64 }}")?;
84        }
85        #[cfg(feature = "nanbox")]
86        {
87            // Nanbox: Single i64 with NaN-boxing encoding (8 bytes)
88            writeln!(&mut ir, "; Value type (NaN-boxed - 8 bytes)")?;
89            writeln!(&mut ir, "%Value = type i64")?;
90        }
91        writeln!(&mut ir)?;
92
93        // String and symbol constants
94        self.emit_string_and_symbol_globals(&mut ir)?;
95
96        // Runtime function declarations
97        emit_runtime_decls(&mut ir)?;
98
99        // External builtin declarations (from config)
100        if !self.external_builtins.is_empty() {
101            writeln!(&mut ir, "; External builtin declarations")?;
102            for symbol in self.external_builtins.values() {
103                // All external builtins follow the standard stack convention: ptr -> ptr
104                writeln!(&mut ir, "declare ptr @{}(ptr)", symbol)?;
105            }
106            writeln!(&mut ir)?;
107        }
108
109        // Quotation functions (generated from quotation literals)
110        if !self.quotation_functions.is_empty() {
111            writeln!(&mut ir, "; Quotation functions")?;
112            ir.push_str(&self.quotation_functions);
113            writeln!(&mut ir)?;
114        }
115
116        // User-defined words and main
117        ir.push_str(&self.output);
118
119        Ok(ir)
120    }
121
122    /// Generate LLVM IR for entire program with FFI support
123    ///
124    /// This is the main entry point for compiling programs that use FFI.
125    pub fn codegen_program_with_ffi(
126        &mut self,
127        program: &Program,
128        type_map: HashMap<usize, Type>,
129        statement_types: HashMap<(String, usize), Type>,
130        config: &CompilerConfig,
131        ffi_bindings: &FfiBindings,
132    ) -> Result<String, CodeGenError> {
133        // Store FFI bindings
134        self.ffi_bindings = ffi_bindings.clone();
135
136        // Generate FFI wrapper functions
137        self.generate_ffi_wrappers()?;
138
139        // Store type map for use during code generation
140        self.type_map = type_map;
141        self.statement_types = statement_types;
142
143        // Store union definitions for pattern matching
144        self.unions = program.unions.clone();
145
146        // Build external builtins map from config
147        self.external_builtins = config
148            .external_builtins
149            .iter()
150            .map(|b| (b.seq_name.clone(), b.symbol.clone()))
151            .collect();
152
153        // Verify we have a main word
154        if program.find_word("main").is_none() {
155            return Err(CodeGenError::Logic("No main word defined".to_string()));
156        }
157
158        // Generate all user-defined words
159        for word in &program.words {
160            self.codegen_word(word)?;
161        }
162
163        // Generate main function
164        self.codegen_main()?;
165
166        // Assemble final IR
167        let mut ir = String::new();
168
169        // Target and type declarations
170        writeln!(&mut ir, "; ModuleID = 'main'")?;
171        writeln!(&mut ir, "target triple = \"{}\"", get_target_triple())?;
172        writeln!(&mut ir)?;
173
174        // Value type definition
175        #[cfg(not(feature = "nanbox"))]
176        {
177            writeln!(&mut ir, "; Value type (Rust enum - 40 bytes)")?;
178            writeln!(&mut ir, "%Value = type {{ i64, i64, i64, i64, i64 }}")?;
179        }
180        #[cfg(feature = "nanbox")]
181        {
182            writeln!(&mut ir, "; Value type (NaN-boxed - 8 bytes)")?;
183            writeln!(&mut ir, "%Value = type i64")?;
184        }
185        writeln!(&mut ir)?;
186
187        // String and symbol constants
188        self.emit_string_and_symbol_globals(&mut ir)?;
189
190        // Runtime function declarations (same as codegen_program_with_config)
191        self.emit_runtime_declarations(&mut ir)?;
192
193        // FFI C function declarations
194        if !self.ffi_bindings.functions.is_empty() {
195            writeln!(&mut ir, "; FFI C function declarations")?;
196            writeln!(&mut ir, "declare ptr @malloc(i64)")?;
197            writeln!(&mut ir, "declare void @free(ptr)")?;
198            writeln!(&mut ir, "declare i64 @strlen(ptr)")?;
199            writeln!(&mut ir, "declare ptr @memcpy(ptr, ptr, i64)")?;
200            // Declare FFI string helpers from runtime
201            writeln!(
202                &mut ir,
203                "declare ptr @patch_seq_string_to_cstring(ptr, ptr)"
204            )?;
205            writeln!(
206                &mut ir,
207                "declare ptr @patch_seq_cstring_to_string(ptr, ptr)"
208            )?;
209            for func in self.ffi_bindings.functions.values() {
210                let c_ret_type = ffi_return_type(&func.return_spec);
211                let c_args = ffi_c_args(&func.args);
212                writeln!(
213                    &mut ir,
214                    "declare {} @{}({})",
215                    c_ret_type, func.c_name, c_args
216                )?;
217            }
218            writeln!(&mut ir)?;
219        }
220
221        // External builtin declarations (from config)
222        if !self.external_builtins.is_empty() {
223            writeln!(&mut ir, "; External builtin declarations")?;
224            for symbol in self.external_builtins.values() {
225                writeln!(&mut ir, "declare ptr @{}(ptr)", symbol)?;
226            }
227            writeln!(&mut ir)?;
228        }
229
230        // FFI wrapper functions
231        if !self.ffi_wrapper_code.is_empty() {
232            writeln!(&mut ir, "; FFI wrapper functions")?;
233            ir.push_str(&self.ffi_wrapper_code);
234            writeln!(&mut ir)?;
235        }
236
237        // Quotation functions
238        if !self.quotation_functions.is_empty() {
239            writeln!(&mut ir, "; Quotation functions")?;
240            ir.push_str(&self.quotation_functions);
241            writeln!(&mut ir)?;
242        }
243
244        // User-defined words and main
245        ir.push_str(&self.output);
246
247        Ok(ir)
248    }
249
250    /// Emit runtime function declarations
251    pub(super) fn emit_runtime_declarations(&self, ir: &mut String) -> Result<(), CodeGenError> {
252        emit_runtime_decls(ir)
253    }
254}