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