Skip to main content

seqc/codegen/specialization/
codegen_word.rs

1//! The top-level specialized codegen: walking a word definition, dispatching
2//! on statement kind, generating the `define … { … }` prologue, lowering
3//! `if / else`, and emitting returns. The per-operation and per-call
4//! emitters live in sibling files (`codegen_ops`, `codegen_safe_math`,
5//! `codegen_calls`).
6//!
7//! All of the per-function work happens inside a [`SpecializedEmitter`],
8//! a short-lived wrapper that owns the function-level constants
9//! (`word_name`, `sig`) so they don't have to be threaded through every
10//! helper signature. It derefs to the underlying [`CodeGen`], so
11//! existing `self.output` / `self.fresh_temp()` access keeps working.
12
13use super::CodeGen;
14use super::context::RegisterContext;
15use super::types::{RegisterType, SpecSignature};
16use crate::ast::{Statement, WordDef};
17use crate::codegen::CodeGenError;
18use crate::codegen::mangle_name;
19use std::fmt::Write as _;
20use std::ops::{Deref, DerefMut};
21
22/// Wraps a [`CodeGen`] borrow with the constants that stay fixed for
23/// one specialized-function compilation. Methods on this type drop
24/// `word_name`/`sig`/`is_last` from the parameter parade — they're
25/// fields, not arguments.
26///
27/// `Deref<Target = CodeGen>` lets the borrowed codegen state (output
28/// buffer, fresh-name counters, side tables) be reached transparently
29/// from the emitter's methods, so the migration from `impl CodeGen` to
30/// `impl SpecializedEmitter` is a parameter cleanup, not a state rewire.
31pub(super) struct SpecializedEmitter<'a> {
32    codegen: &'a mut CodeGen,
33    word_name: &'a str,
34    sig: &'a SpecSignature,
35}
36
37impl<'a> SpecializedEmitter<'a> {
38    pub(super) fn new(
39        codegen: &'a mut CodeGen,
40        word_name: &'a str,
41        sig: &'a SpecSignature,
42    ) -> Self {
43        Self {
44            codegen,
45            word_name,
46            sig,
47        }
48    }
49
50    /// The name of the word being compiled. Set once per emitter.
51    pub(super) fn word_name(&self) -> &str {
52        self.word_name
53    }
54
55    /// The specialization signature of the word being compiled. Set
56    /// once per emitter.
57    pub(super) fn sig(&self) -> &SpecSignature {
58        self.sig
59    }
60}
61
62impl Deref for SpecializedEmitter<'_> {
63    type Target = CodeGen;
64    fn deref(&self) -> &CodeGen {
65        self.codegen
66    }
67}
68
69impl DerefMut for SpecializedEmitter<'_> {
70    fn deref_mut(&mut self) -> &mut CodeGen {
71        self.codegen
72    }
73}
74
75impl CodeGen {
76    /// Generate a specialized version of a word.
77    ///
78    /// This creates a register-based function that passes values directly in
79    /// CPU registers instead of through the tagged pointer stack.
80    ///
81    /// The generated function:
82    /// - Takes primitive arguments directly (i64 for Int/Bool, double for Float)
83    /// - Returns the result in a register (not via stack pointer)
84    /// - Uses `musttail` for recursive calls to guarantee TCO
85    /// - Handles control flow with phi nodes for value merging
86    pub fn codegen_specialized_word(
87        &mut self,
88        word: &WordDef,
89        sig: &SpecSignature,
90    ) -> Result<(), CodeGenError> {
91        SpecializedEmitter::new(self, &word.name, sig).emit_word(word)
92    }
93}
94
95impl SpecializedEmitter<'_> {
96    /// Emit the full specialized function: signature, entry block,
97    /// statements, and register the word in `specialized_words`.
98    fn emit_word(&mut self, word: &WordDef) -> Result<(), CodeGenError> {
99        let base_name = format!("seq_{}", mangle_name(self.word_name));
100        let spec_name = format!("{}{}", base_name, self.sig.suffix());
101
102        // Generate function signature
103        // For single output: define i64 @name(i64 %arg0) {
104        // For multiple outputs: define { i64, i64 } @name(i64 %arg0, i64 %arg1) {
105        let return_type = if self.sig.outputs.len() == 1 {
106            self.sig.outputs[0].llvm_type().to_string()
107        } else {
108            let types: Vec<_> = self.sig.outputs.iter().map(|t| t.llvm_type()).collect();
109            format!("{{ {} }}", types.join(", "))
110        };
111
112        let params: Vec<String> = self
113            .sig
114            .inputs
115            .iter()
116            .enumerate()
117            .map(|(i, ty)| format!("{} %arg{}", ty.llvm_type(), i))
118            .collect();
119
120        writeln!(
121            &mut self.output,
122            "define {} @{}({}) {{",
123            return_type,
124            spec_name,
125            params.join(", ")
126        )?;
127        writeln!(&mut self.output, "entry:")?;
128
129        let initial_params: Vec<(String, RegisterType)> = self
130            .sig
131            .inputs
132            .iter()
133            .enumerate()
134            .map(|(i, ty)| (format!("arg{}", i), *ty))
135            .collect();
136        let mut ctx = RegisterContext::from_params(&initial_params);
137
138        let body_len = word.body.len();
139        let mut prev_int_literal: Option<i64> = None;
140        for (i, stmt) in word.body.iter().enumerate() {
141            let is_last = i == body_len - 1;
142            self.emit_statement(&mut ctx, stmt, is_last, &mut prev_int_literal)?;
143        }
144
145        writeln!(&mut self.output, "}}")?;
146        writeln!(&mut self.output)?;
147
148        // Record that this word is specialized. (Bind locals first: the
149        // `insert` call needs `&mut self.specialized_words` via DerefMut,
150        // which clashes with reading `self.word_name`/`self.sig` in the
151        // same expression.)
152        let key = self.word_name.to_string();
153        let sig = self.sig.clone();
154        self.specialized_words.insert(key, sig);
155
156        Ok(())
157    }
158
159    /// Generate specialized code for a single statement.
160    pub(super) fn emit_statement(
161        &mut self,
162        ctx: &mut RegisterContext,
163        stmt: &Statement,
164        is_last: bool,
165        prev_int_literal: &mut Option<i64>,
166    ) -> Result<(), CodeGenError> {
167        // Track previous int literal for pick/roll optimization
168        let prev_int = *prev_int_literal;
169        *prev_int_literal = None; // Reset unless this is an IntLiteral
170
171        match stmt {
172            Statement::IntLiteral(n) => {
173                let var = self.fresh_temp();
174                writeln!(&mut self.output, "  %{} = add i64 0, {}", var, n)?;
175                ctx.push(var, RegisterType::I64);
176                *prev_int_literal = Some(*n); // Track for next statement
177            }
178
179            Statement::FloatLiteral(f) => {
180                let var = self.fresh_temp();
181                // Use bitcast from integer bits for exact IEEE 754 representation.
182                // This avoids precision loss from decimal string conversion (e.g., 0.1
183                // cannot be exactly represented in binary floating point). By storing
184                // the raw bit pattern and using bitcast, we preserve the exact value.
185                let bits = f.to_bits();
186                writeln!(
187                    &mut self.output,
188                    "  %{} = bitcast i64 {} to double",
189                    var, bits
190                )?;
191                ctx.push(var, RegisterType::Double);
192            }
193
194            Statement::BoolLiteral(b) => {
195                let var = self.fresh_temp();
196                let val = if *b { 1 } else { 0 };
197                writeln!(&mut self.output, "  %{} = add i64 0, {}", var, val)?;
198                ctx.push(var, RegisterType::I64);
199            }
200
201            Statement::WordCall { name, .. } => {
202                self.emit_word_call(ctx, name, is_last, prev_int)?;
203            }
204
205            Statement::If {
206                then_branch,
207                else_branch,
208                span: _,
209            } => {
210                self.emit_if(ctx, then_branch, else_branch.as_deref(), is_last)?;
211            }
212
213            // These shouldn't appear in specializable words (checked in can_specialize)
214            Statement::StringLiteral(_)
215            | Statement::Symbol(_)
216            | Statement::Quotation { .. }
217            | Statement::Match { .. } => {
218                return Err(CodeGenError::Logic(format!(
219                    "Non-specializable statement in specialized word: {:?}",
220                    stmt
221                )));
222            }
223        }
224
225        // Emit return if this is the last statement and it's not a control flow op
226        // that already emits returns (like if, or recursive calls)
227        let already_returns = match stmt {
228            Statement::If { .. } => true,
229            Statement::WordCall { name, .. } if name == self.word_name => true,
230            _ => false,
231        };
232        if is_last && !already_returns {
233            self.emit_return(ctx)?;
234        }
235
236        Ok(())
237    }
238
239    /// Emit return statement for specialized function.
240    pub(super) fn emit_return(&mut self, ctx: &RegisterContext) -> Result<(), CodeGenError> {
241        let output_count = self.sig.outputs.len();
242
243        if output_count == 0 {
244            writeln!(&mut self.output, "  ret void")?;
245        } else if output_count == 1 {
246            let (var, ty) = ctx
247                .values
248                .last()
249                .ok_or_else(|| CodeGenError::Logic("Empty context at return".to_string()))?;
250            writeln!(&mut self.output, "  ret {} %{}", ty.llvm_type(), var)?;
251        } else {
252            // Multi-output: build struct return.
253            // Values in context are bottom-to-top, matching sig.outputs order.
254            if ctx.values.len() < output_count {
255                return Err(CodeGenError::Logic(format!(
256                    "Not enough values for multi-output return: need {}, have {}",
257                    output_count,
258                    ctx.values.len()
259                )));
260            }
261
262            let start_idx = ctx.values.len() - output_count;
263            let return_values: Vec<_> = ctx.values[start_idx..].to_vec();
264
265            let struct_type = self.sig.llvm_return_type();
266
267            let mut current_struct = "undef".to_string();
268            for (i, (var, ty)) in return_values.iter().enumerate() {
269                let new_struct = self.fresh_temp();
270                writeln!(
271                    &mut self.output,
272                    "  %{} = insertvalue {} {}, {} %{}, {}",
273                    new_struct,
274                    struct_type,
275                    current_struct,
276                    ty.llvm_type(),
277                    var,
278                    i
279                )?;
280                current_struct = format!("%{}", new_struct);
281            }
282
283            writeln!(&mut self.output, "  ret {} {}", struct_type, current_struct)?;
284        }
285        Ok(())
286    }
287
288    /// Emit code for one branch of a specialized if-statement.
289    ///
290    /// Writes the branch's label, processes its statements on a cloned
291    /// context, and either lets the branch's last statement emit the
292    /// function's return (when `is_last`) or emits a `br` to
293    /// `merge_label`.
294    ///
295    /// Returns `(branch_ctx, predecessor)` — `predecessor` is `Some` if
296    /// the branch falls through to `merge_label` (and therefore feeds a
297    /// phi node), `None` if it already returned.
298    fn emit_branch(
299        &mut self,
300        parent_ctx: &RegisterContext,
301        branch: &[Statement],
302        branch_label: &str,
303        merge_label: &str,
304        is_last: bool,
305    ) -> Result<(RegisterContext, Option<String>), CodeGenError> {
306        writeln!(&mut self.output, "{}:", branch_label)?;
307        let mut branch_ctx = parent_ctx.clone();
308        let mut branch_prev_int: Option<i64> = None;
309        for (i, stmt) in branch.iter().enumerate() {
310            let is_stmt_last = i == branch.len() - 1 && is_last;
311            self.emit_statement(&mut branch_ctx, stmt, is_stmt_last, &mut branch_prev_int)?;
312        }
313        // Empty branch (or no-else) needs its return emitted explicitly:
314        // there was no last statement to do it via the in-statement path.
315        if is_last && branch.is_empty() {
316            self.emit_return(&branch_ctx)?;
317        }
318        let predecessor = if is_last {
319            None
320        } else {
321            writeln!(&mut self.output, "  br label %{}", merge_label)?;
322            Some(branch_label.to_string())
323        };
324        Ok((branch_ctx, predecessor))
325    }
326
327    /// Generate specialized if/else statement.
328    pub(super) fn emit_if(
329        &mut self,
330        ctx: &mut RegisterContext,
331        then_branch: &[Statement],
332        else_branch: Option<&[Statement]>,
333        is_last: bool,
334    ) -> Result<(), CodeGenError> {
335        let (cond_var, _) = ctx
336            .pop()
337            .ok_or_else(|| CodeGenError::Logic("Empty context at if condition".to_string()))?;
338
339        let cmp_result = self.fresh_temp();
340        writeln!(
341            &mut self.output,
342            "  %{} = icmp ne i64 %{}, 0",
343            cmp_result, cond_var
344        )?;
345
346        let then_label = self.fresh_block("if_then");
347        let else_label = self.fresh_block("if_else");
348        let merge_label = self.fresh_block("if_merge");
349
350        writeln!(
351            &mut self.output,
352            "  br i1 %{}, label %{}, label %{}",
353            cmp_result, then_label, else_label
354        )?;
355
356        let (then_ctx, then_pred) =
357            self.emit_branch(ctx, then_branch, &then_label, &merge_label, is_last)?;
358
359        // None or empty else is the same shape from the helper's view.
360        let else_slice: &[Statement] = else_branch.unwrap_or(&[]);
361        let (else_ctx, else_pred) =
362            self.emit_branch(ctx, else_slice, &else_label, &merge_label, is_last)?;
363
364        // Merge block with phi nodes if either branch continues
365        if then_pred.is_some() || else_pred.is_some() {
366            writeln!(&mut self.output, "{}:", merge_label)?;
367
368            if let (Some(then_p), Some(else_p)) = (&then_pred, &else_pred) {
369                // Both branches continue - merge all values with phi nodes
370                if then_ctx.values.len() != else_ctx.values.len() {
371                    return Err(CodeGenError::Logic(format!(
372                        "Stack depth mismatch in if branches: then has {}, else has {}",
373                        then_ctx.values.len(),
374                        else_ctx.values.len()
375                    )));
376                }
377
378                ctx.values.clear();
379                for i in 0..then_ctx.values.len() {
380                    let (then_var, then_ty) = &then_ctx.values[i];
381                    let (else_var, else_ty) = &else_ctx.values[i];
382
383                    if then_ty != else_ty {
384                        return Err(CodeGenError::Logic(format!(
385                            "Type mismatch at position {} in if branches: {:?} vs {:?}",
386                            i, then_ty, else_ty
387                        )));
388                    }
389
390                    if then_var == else_var {
391                        ctx.push(then_var.clone(), *then_ty);
392                    } else {
393                        let phi_result = self.fresh_temp();
394                        writeln!(
395                            &mut self.output,
396                            "  %{} = phi {} [ %{}, %{} ], [ %{}, %{} ]",
397                            phi_result,
398                            then_ty.llvm_type(),
399                            then_var,
400                            then_p,
401                            else_var,
402                            else_p
403                        )?;
404                        ctx.push(phi_result, *then_ty);
405                    }
406                }
407            } else if then_pred.is_some() {
408                *ctx = then_ctx;
409            } else {
410                *ctx = else_ctx;
411            }
412
413            if is_last && (then_pred.is_some() || else_pred.is_some()) {
414                self.emit_return(ctx)?;
415            }
416        }
417
418        Ok(())
419    }
420}