1use std::collections::HashMap;
2
3use tupa_parser::{Expr, ExprKind, Function, Item, Program, Stmt, Type};
4pub mod execution_plan;
5#[allow(unused_imports)]
6use tupa_typecheck::{typecheck_program_with_warnings, Ty};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9enum SimpleTy {
10 I64,
11 F64,
12 Bool,
13 I64Ptr,
14 F64Ptr,
15 Str,
16 StrPtr,
17 BoolPtr,
18 Void,
19 ClosurePtr,
20 Unknown,
21}
22
23pub fn generate_stub(program: &Program) -> String {
24 let mut codegen = Codegen::default();
25 codegen.emit_program(program);
26 codegen.finish()
27}
28
29pub fn generate_stub_with_types(program: &Program) -> String {
30 let mut codegen = Codegen {
31 type_info: Some(HashMap::new()),
32 ..Default::default()
33 };
34 codegen.emit_program(program);
35 codegen.finish()
36}
37
38#[derive(Default)]
39struct Codegen {
40 lines: Vec<String>,
41 globals: Vec<String>,
42 string_pool: HashMap<String, (String, usize)>,
43 temp: usize,
44 label: usize,
45 loop_stack: Vec<LoopLabels>,
46 fmt_int_emitted: bool,
47 fmt_float_emitted: bool,
48 printf_declared: bool,
49 puts_declared: bool,
50 strcmp_declared: bool,
51 strlen_declared: bool,
52 malloc_declared: bool,
53 strcpy_declared: bool,
54 strcat_declared: bool,
55 snprintf_declared: bool,
56 powf_declared: bool,
57 function_sigs: HashMap<String, FuncSig>,
58 used_vars: HashMap<String, bool>,
59 #[allow(dead_code)]
60 type_info: Option<HashMap<String, tupa_typecheck::Ty>>,
61}
62
63#[derive(Debug, Clone)]
64struct LocalVar {
65 ptr: String,
66 ty: SimpleTy,
67 used: bool,
68}
69
70#[allow(dead_code)]
71#[derive(Debug, Clone)]
72struct ClosureEnv {
73 vars: Vec<(String, SimpleTy)>, struct_name: String,
75}
76
77#[derive(Debug, Clone)]
78struct LoopLabels {
79 break_label: String,
80 continue_label: String,
81}
82
83#[derive(Debug, Clone)]
84struct FuncSig {
85 #[allow(dead_code)]
86 params: Vec<SimpleTy>,
87 ret: SimpleTy,
88}
89
90impl Codegen {
91 fn declare_snprintf(&mut self) {
92 if !self.snprintf_declared {
93 self.globals
94 .push("declare i32 @snprintf(i8*, i64, i8*, ...)".to_string());
95 self.snprintf_declared = true;
96 }
97 }
98
99 fn declare_powf(&mut self) {
100 if !self.powf_declared {
101 self.globals
102 .push("declare double @llvm.pow.f64(double, double)".to_string());
103 self.powf_declared = true;
104 }
105 }
106 fn emit_string_concat(&mut self, left: ExprValue, right: ExprValue) -> ExprValue {
108 self.declare_strlen();
109 self.declare_malloc();
110 self.declare_strcpy();
111 self.declare_strcat();
112 let len_left = self.fresh_temp();
114 self.lines.push(format!(
115 " {len_left} = call i64 @strlen(i8* {})",
116 left.llvm_value
117 ));
118 let len_right = self.fresh_temp();
120 self.lines.push(format!(
121 " {len_right} = call i64 @strlen(i8* {})",
122 right.llvm_value
123 ));
124 let total = self.fresh_temp();
126 self.lines
127 .push(format!(" {total} = add i64 {len_left}, {len_right}"));
128 let total1 = self.fresh_temp();
129 self.lines.push(format!(" {total1} = add i64 {total}, 1"));
130 let buf = self.fresh_temp();
132 self.lines
133 .push(format!(" {buf} = call i8* @malloc(i64 {total1})"));
134 self.lines.push(format!(
136 " call i8* @strcpy(i8* {buf}, i8* {})",
137 left.llvm_value
138 ));
139 self.lines.push(format!(
141 " call i8* @strcat(i8* {buf}, i8* {})",
142 right.llvm_value
143 ));
144 ExprValue::new(SimpleTy::Str, buf)
145 }
146
147 fn emit_format_value(&mut self, val: ExprValue) -> Option<ExprValue> {
149 match val.ty {
150 SimpleTy::I64 => {
151 self.declare_printf();
152 let (fmt, len) = self.intern_format_int();
153 let fmt_ptr = self.fresh_temp();
154 self.lines.push(format!(" {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
155 self.declare_malloc();
157 let buf = self.fresh_temp();
158 self.lines
159 .push(format!(" {buf} = call i8* @malloc(i64 32)"));
160 self.declare_snprintf();
162 self.lines.push(format!(
163 " call i32 @snprintf(i8* {buf}, i64 32, i8* {fmt_ptr}, i64 {})",
164 val.llvm_value
165 ));
166 Some(ExprValue::new(SimpleTy::Str, buf))
167 }
168 SimpleTy::F64 => {
169 self.declare_printf();
170 let (fmt, len) = self.intern_format_float();
171 let fmt_ptr = self.fresh_temp();
172 self.lines.push(format!(" {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
173 self.declare_malloc();
174 let buf = self.fresh_temp();
175 self.lines
176 .push(format!(" {buf} = call i8* @malloc(i64 32)"));
177 self.declare_snprintf();
178 self.lines.push(format!(
179 " call i32 @snprintf(i8* {buf}, i64 32, i8* {fmt_ptr}, double {})",
180 val.llvm_value
181 ));
182 Some(ExprValue::new(SimpleTy::Str, buf))
183 }
184 SimpleTy::Bool => {
185 let true_str = self.intern_string("true").0;
187 let false_str = self.intern_string("false").0;
188 let select = self.fresh_temp();
189 self.lines.push(format!(
190 " {select} = select i1 {}, i8* {}, i8* {}",
191 val.llvm_value, true_str, false_str
192 ));
193 Some(ExprValue::new(SimpleTy::Str, select))
194 }
195 SimpleTy::Str => Some(val),
196 _ => None,
197 }
198 }
199 fn emit_program(&mut self, program: &Program) {
200 for item in &program.items {
201 let func = match item {
202 Item::Function(func) => func,
203 Item::Enum(_) => continue, Item::Trait(_) => continue, Item::Pipeline(_) => continue, };
207 let params = func
208 .params
209 .iter()
210 .map(|p| self.simple_ty_from_type(&p.ty))
211 .collect::<Vec<_>>();
212 let ret = match func.return_type.as_ref() {
213 Some(ty) => self.simple_ty_from_type(ty),
214 None => SimpleTy::Void,
215 };
216 self.function_sigs
217 .insert(func.name.clone(), FuncSig { params, ret });
218 }
219 for item in &program.items {
220 match item {
221 Item::Function(func) => self.emit_function(func),
222 Item::Enum(_) => {} Item::Trait(_) => {} Item::Pipeline(_) => {} }
226 }
227 }
228
229 fn is_var_used(&self, name: &str) -> bool {
230 self.used_vars.get(name).copied().unwrap_or(true)
231 }
232
233 fn collect_used_vars(&self, func: &Function) -> HashMap<String, bool> {
234 let mut env = HashMap::new();
235 for param in &func.params {
236 env.insert(param.name.clone(), false);
237 }
238 self.collect_used_in_block(&func.body, &mut env);
239 env
240 }
241
242 fn merge_used_vars(&self, parent: &mut HashMap<String, bool>, child: &HashMap<String, bool>) {
243 for (name, used) in parent.iter_mut() {
244 if let Some(child_used) = child.get(name) {
245 if *child_used {
246 *used = true;
247 }
248 }
249 }
250 }
251
252 fn collect_used_in_block(&self, stmts: &[Stmt], env: &mut HashMap<String, bool>) {
253 for stmt in stmts {
254 self.collect_used_in_stmt(stmt, env);
255 }
256 }
257
258 fn collect_used_in_stmt(&self, stmt: &Stmt, env: &mut HashMap<String, bool>) {
259 match stmt {
260 Stmt::Let { name, expr, .. } => {
261 self.collect_used_in_expr(expr, env);
262 env.insert(name.clone(), false);
263 }
264 Stmt::Expr(expr) => {
265 self.collect_used_in_expr(expr, env);
266 }
267 Stmt::Return(expr) => {
268 if let Some(expr) = expr {
269 self.collect_used_in_expr(expr, env);
270 }
271 }
272 Stmt::While { condition, body } => {
273 self.collect_used_in_expr(condition, env);
274 let mut inner = env.clone();
275 self.collect_used_in_block(body, &mut inner);
276 self.merge_used_vars(env, &inner);
277 }
278 Stmt::For { name, iter, body } => {
279 self.collect_used_in_expr(iter, env);
280 let mut inner = env.clone();
281 inner.insert(name.clone(), false);
282 self.collect_used_in_block(body, &mut inner);
283 self.merge_used_vars(env, &inner);
284 }
285 Stmt::Break | Stmt::Continue => {}
286 Stmt::Lambda { body, .. } => {
287 self.collect_used_in_expr(body, env);
288 }
289 }
290 }
291
292 fn fold_expr(&self, expr: &Expr) -> Expr {
293 match &expr.kind {
294 ExprKind::Binary { op, left, right } => {
295 let folded_left = self.fold_expr(left);
296 let folded_right = self.fold_expr(right);
297
298 match (&folded_left.kind, &folded_right.kind, op) {
299 (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Add) => Expr {
300 kind: ExprKind::Int(a + b),
301 span: expr.span,
302 },
303 (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Sub) => Expr {
304 kind: ExprKind::Int(a - b),
305 span: expr.span,
306 },
307 (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Mul) => Expr {
308 kind: ExprKind::Int(a * b),
309 span: expr.span,
310 },
311 (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Div) if *b != 0 => {
312 Expr {
313 kind: ExprKind::Int(a / b),
314 span: expr.span,
315 }
316 }
317 (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Pow) => {
318 if *b >= 0 {
319 let Ok(exp) = u32::try_from(*b) else {
320 return Expr {
321 kind: ExprKind::Binary {
322 op: op.clone(),
323 left: Box::new(folded_left),
324 right: Box::new(folded_right),
325 },
326 span: expr.span,
327 };
328 };
329 Expr {
330 kind: ExprKind::Int(a.pow(exp)),
331 span: expr.span,
332 }
333 } else {
334 Expr {
335 kind: ExprKind::Binary {
336 op: op.clone(),
337 left: Box::new(folded_left),
338 right: Box::new(folded_right),
339 },
340 span: expr.span,
341 }
342 }
343 }
344 (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Add) => Expr {
345 kind: ExprKind::Float(a + b),
346 span: expr.span,
347 },
348 (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Sub) => Expr {
349 kind: ExprKind::Float(a - b),
350 span: expr.span,
351 },
352 (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Mul) => Expr {
353 kind: ExprKind::Float(a * b),
354 span: expr.span,
355 },
356 (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Div)
357 if *b != 0.0 =>
358 {
359 Expr {
360 kind: ExprKind::Float(a / b),
361 span: expr.span,
362 }
363 }
364 (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Pow) => Expr {
365 kind: ExprKind::Float(a.powf(*b)),
366 span: expr.span,
367 },
368 (ExprKind::Bool(a), ExprKind::Bool(b), tupa_parser::BinaryOp::And) => Expr {
369 kind: ExprKind::Bool(*a && *b),
370 span: expr.span,
371 },
372 (ExprKind::Bool(a), ExprKind::Bool(b), tupa_parser::BinaryOp::Or) => Expr {
373 kind: ExprKind::Bool(*a || *b),
374 span: expr.span,
375 },
376 _ => Expr {
377 kind: ExprKind::Binary {
378 op: op.clone(),
379 left: Box::new(folded_left),
380 right: Box::new(folded_right),
381 },
382 span: expr.span,
383 },
384 }
385 }
386 ExprKind::Unary { op, expr: operand } => {
387 let folded_operand = self.fold_expr(operand);
388
389 match (&folded_operand.kind, op) {
390 (ExprKind::Int(a), tupa_parser::UnaryOp::Neg) => Expr {
391 kind: ExprKind::Int(-a),
392 span: expr.span,
393 },
394 (ExprKind::Float(a), tupa_parser::UnaryOp::Neg) => Expr {
395 kind: ExprKind::Float(-a),
396 span: expr.span,
397 },
398 (ExprKind::Bool(a), tupa_parser::UnaryOp::Not) => Expr {
399 kind: ExprKind::Bool(!a),
400 span: expr.span,
401 },
402 _ => Expr {
403 kind: ExprKind::Unary {
404 op: op.clone(),
405 expr: Box::new(folded_operand),
406 },
407 span: expr.span,
408 },
409 }
410 }
411 _ => expr.clone(),
412 }
413 }
414
415 fn collect_used_in_expr(&self, expr: &Expr, env: &mut HashMap<String, bool>) {
416 match &expr.kind {
417 ExprKind::Int(_)
418 | ExprKind::Float(_)
419 | ExprKind::Str(_)
420 | ExprKind::Bool(_)
421 | ExprKind::Null => {}
422 ExprKind::Ident(name) => {
423 if let Some(used) = env.get_mut(name) {
424 *used = true;
425 }
426 }
427 ExprKind::Binary { left, right, .. } => {
428 self.collect_used_in_expr(left, env);
429 self.collect_used_in_expr(right, env);
430 }
431 ExprKind::Unary { expr: operand, .. } => {
432 self.collect_used_in_expr(operand, env);
433 }
434 ExprKind::Call { callee, args } => {
435 self.collect_used_in_expr(callee, env);
436 for arg in args {
437 self.collect_used_in_expr(arg, env);
438 }
439 }
440 ExprKind::Assign { name: _name, expr } => {
441 self.collect_used_in_expr(expr, env);
442 }
443 ExprKind::Index { expr: array, index } => {
444 self.collect_used_in_expr(array, env);
445 self.collect_used_in_expr(index, env);
446 }
447 ExprKind::ArrayLiteral(elements) => {
448 for elem in elements {
449 self.collect_used_in_expr(elem, env);
450 }
451 }
452 ExprKind::Tuple(items) => {
453 for item in items {
454 self.collect_used_in_expr(item, env);
455 }
456 }
457 ExprKind::RecordLiteral(fields) => {
458 for (_, value) in fields {
459 self.collect_used_in_expr(value, env);
460 }
461 }
462 ExprKind::Field { expr: base, .. } => {
463 self.collect_used_in_expr(base, env);
464 }
465 ExprKind::Lambda { params, body } => {
466 let mut inner = env.clone();
467 for param in params {
468 inner.insert(param.clone(), false);
469 }
470 self.collect_used_in_expr(body, &mut inner);
471 self.merge_used_vars(env, &inner);
472 }
473 ExprKind::Block(stmts) => {
474 let mut inner = env.clone();
475 self.collect_used_in_block(stmts, &mut inner);
476 self.merge_used_vars(env, &inner);
477 }
478 ExprKind::If {
479 condition,
480 then_branch,
481 else_branch,
482 } => {
483 self.collect_used_in_expr(condition, env);
484 let mut then_env = env.clone();
485 self.collect_used_in_block(then_branch, &mut then_env);
486 if let Some(else_branch) = else_branch {
487 let mut else_env = env.clone();
488 match else_branch {
489 tupa_parser::ElseBranch::Block(block) => {
490 self.collect_used_in_block(block, &mut else_env)
491 }
492 tupa_parser::ElseBranch::If(expr) => {
493 self.collect_used_in_expr(expr, &mut else_env)
494 }
495 }
496 self.merge_used_vars(env, &then_env);
497 self.merge_used_vars(env, &else_env);
498 } else {
499 self.merge_used_vars(env, &then_env);
500 }
501 }
502 ExprKind::AssignIndex {
503 expr: array,
504 index,
505 value,
506 } => {
507 self.collect_used_in_expr(array, env);
508 self.collect_used_in_expr(index, env);
509 self.collect_used_in_expr(value, env);
510 }
511 ExprKind::Await(expr) => {
512 self.collect_used_in_expr(expr, env);
513 }
514 ExprKind::Match {
515 expr: match_expr,
516 arms,
517 } => {
518 self.collect_used_in_expr(match_expr, env);
519 for arm in arms {
520 let mut arm_env = env.clone();
521 if let tupa_parser::Pattern::Ident(name) = &arm.pattern {
522 arm_env.insert(name.clone(), false);
523 }
524 if let Some(guard) = &arm.guard {
525 self.collect_used_in_expr(guard, &mut arm_env);
526 }
527 self.collect_used_in_expr(&arm.expr, &mut arm_env);
528 self.merge_used_vars(env, &arm_env);
529 }
530 }
531 }
532 }
533
534 fn emit_function(&mut self, func: &Function) {
535 let mut env: HashMap<String, LocalVar> = HashMap::new();
536 self.used_vars = self.collect_used_vars(func);
537 let ret_ty = match func.return_type.as_ref() {
538 Some(Type::Ident(name)) if name == "i64" => SimpleTy::I64,
539 Some(Type::Ident(name)) if name == "f64" => SimpleTy::F64,
540 Some(Type::Ident(name)) if name == "bool" => SimpleTy::Bool,
541 Some(Type::Ident(name)) if name == "string" => SimpleTy::Str,
542 Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
543 SimpleTy::I64Ptr
544 }
545 Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
546 SimpleTy::F64Ptr
547 }
548 Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "string") => {
549 SimpleTy::StrPtr
550 }
551 Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
552 SimpleTy::BoolPtr
553 }
554 Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
555 SimpleTy::I64Ptr
556 }
557 Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
558 SimpleTy::F64Ptr
559 }
560 Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "string") => {
561 SimpleTy::StrPtr
562 }
563 Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
564 SimpleTy::BoolPtr
565 }
566 Some(_) => SimpleTy::Unknown,
567 None => SimpleTy::Void,
568 };
569 let llvm_ret = match ret_ty {
570 SimpleTy::I64 => "i64",
571 SimpleTy::F64 => "double",
572 SimpleTy::Bool => "i1",
573 SimpleTy::I64Ptr => "i64*",
574 SimpleTy::F64Ptr => "double*",
575 SimpleTy::Str => "i8*",
576 SimpleTy::StrPtr => "i8**",
577 SimpleTy::BoolPtr => "i1*",
578 _ => "void",
579 };
580
581 let params = func
582 .params
583 .iter()
584 .map(|p| {
585 let ty = self.map_type(&p.ty);
586 format!("{} %{}", ty, p.name)
587 })
588 .collect::<Vec<_>>()
589 .join(", ");
590
591 self.lines
592 .push(format!("define {llvm_ret} @{}({params}) {{", func.name));
593 self.lines.push("entry:".to_string());
594
595 for param in &func.params {
596 if !self.is_var_used(¶m.name) {
597 continue;
598 }
599 let ty = match self.map_type(¶m.ty).as_str() {
600 "i64" => SimpleTy::I64,
601 "double" => SimpleTy::F64,
602 "i1" => SimpleTy::Bool,
603 "i64*" => SimpleTy::I64Ptr,
604 "double*" => SimpleTy::F64Ptr,
605 "i8*" => SimpleTy::Str,
606 "i8**" => SimpleTy::StrPtr,
607 "i1*" => SimpleTy::BoolPtr,
608 _ => SimpleTy::Unknown,
609 };
610 let ptr = format!("%{}", param.name);
611 let alloca = self.fresh_temp();
612 self.lines
613 .push(format!(" {alloca} = alloca {}", self.map_type(¶m.ty)));
614 self.lines.push(format!(
615 " store {} {ptr}, {}* {alloca}",
616 self.map_type(¶m.ty),
617 self.map_type(¶m.ty)
618 ));
619 env.insert(
620 param.name.clone(),
621 LocalVar {
622 ptr: alloca,
623 ty,
624 used: false,
625 },
626 );
627 }
628
629 let mut returned = false;
630 let last_index = func.body.len().saturating_sub(1);
631 for (idx, stmt) in func.body.iter().enumerate() {
632 if returned {
633 break;
634 }
635 if idx == last_index {
636 if let Stmt::Expr(expr) = stmt {
637 if ret_ty != SimpleTy::Void {
638 let value = self.emit_expr(expr, &mut env);
639 let llvm_ty = self.llvm_ty(value.ty);
640 self.lines
641 .push(format!(" ret {llvm_ty} {}", value.llvm_value));
642 returned = true;
643 break;
644 }
645 }
646 }
647 if self.emit_stmt(stmt, &mut env, ret_ty) == ControlFlow::Return {
648 returned = true;
649 }
650 }
651
652 if returned {
653 self.lines.push("}".to_string());
654 self.lines.push(String::new());
655 return;
656 }
657
658 if ret_ty == SimpleTy::I64 {
659 self.lines.push(" ret i64 0".to_string());
660 } else if ret_ty == SimpleTy::F64 {
661 self.lines.push(" ret double 0.0".to_string());
662 } else if ret_ty == SimpleTy::Bool {
663 self.lines.push(" ret i1 0".to_string());
664 } else if ret_ty == SimpleTy::I64Ptr {
665 self.lines.push(" ret i64* null".to_string());
666 } else if ret_ty == SimpleTy::F64Ptr {
667 self.lines.push(" ret double* null".to_string());
668 } else if ret_ty == SimpleTy::Str {
669 self.lines.push(" ret i8* null".to_string());
670 } else if ret_ty == SimpleTy::StrPtr {
671 self.lines.push(" ret i8** null".to_string());
672 } else if ret_ty == SimpleTy::BoolPtr {
673 self.lines.push(" ret i1* null".to_string());
674 } else {
675 self.lines.push(" ret void".to_string());
676 }
677 self.lines.push("}".to_string());
678 self.lines.push(String::new());
679 }
680
681 fn emit_stmt(
682 &mut self,
683 stmt: &Stmt,
684 env: &mut HashMap<String, LocalVar>,
685 ret_ty: SimpleTy,
686 ) -> ControlFlow {
687 match stmt {
688 Stmt::Let { name, expr, .. } => {
689 if !self.is_var_used(name) {
690 self.emit_expr(expr, env);
691 return ControlFlow::None;
692 }
693 let value = self.emit_expr(expr, env);
694 let alloca = self.fresh_temp();
695 let llvm_ty = self.llvm_ty(value.ty);
696 self.lines.push(format!(" {alloca} = alloca {llvm_ty}"));
697 self.lines.push(format!(
698 " store {llvm_ty} {}, {llvm_ty}* {alloca}",
699 value.llvm_value
700 ));
701 env.insert(
702 name.clone(),
703 LocalVar {
704 ptr: alloca,
705 ty: value.ty,
706 used: false,
707 },
708 );
709 ControlFlow::None
710 }
711 Stmt::While { condition, body } => {
712 let head = self.fresh_label("while.head");
713 let body_label = self.fresh_label("while.body");
714 let end_label = self.fresh_label("while.end");
715
716 self.loop_stack.push(LoopLabels {
717 break_label: end_label.clone(),
718 continue_label: head.clone(),
719 });
720
721 self.lines.push(format!(" br label %{head}"));
722 self.lines.push(format!("{head}:"));
723 let cond_val = self.emit_expr(condition, env);
724 let cond = if cond_val.ty == SimpleTy::Bool {
725 cond_val.llvm_value
726 } else {
727 "0".to_string()
728 };
729 self.lines.push(format!(
730 " br i1 {cond}, label %{body_label}, label %{end_label}"
731 ));
732 self.lines.push(format!("{body_label}:"));
733 let mut body_used = HashMap::new();
734 self.collect_used_in_block(body, &mut body_used);
735 let previous_used = std::mem::take(&mut self.used_vars);
736 self.used_vars = body_used;
737 let body_flow = self.emit_block(body, env, ret_ty);
738 self.used_vars = previous_used;
739 let body_terminates = matches!(
740 body_flow,
741 ControlFlow::Break | ControlFlow::Continue | ControlFlow::Return
742 );
743 if !body_terminates {
744 self.lines.push(format!(" br label %{head}"));
745 }
746 self.lines.push(format!("{end_label}:"));
747 self.loop_stack.pop();
748 match body_flow {
749 ControlFlow::Return => ControlFlow::Return,
750 _ => ControlFlow::None,
751 }
752 }
753 Stmt::For { name, iter, body } => {
754 let (start, end) = match self.extract_range(iter, env) {
755 Some(range) => range,
756 None => {
757 return ControlFlow::None;
759 }
760 };
761
762 let mut body_used = HashMap::new();
763 body_used.insert(name.clone(), false);
764 self.collect_used_in_block(body, &mut body_used);
765 let should_bind = body_used.get(name).copied().unwrap_or(false);
766 let previous_used = std::mem::take(&mut self.used_vars);
767 self.used_vars = body_used;
768
769 let idx_alloca = self.fresh_temp();
770 self.lines.push(" ; for-range".to_string());
771 self.lines.push(format!(" {idx_alloca} = alloca i64"));
772 self.lines.push(format!(
773 " store i64 {}, i64* {idx_alloca}",
774 start.llvm_value
775 ));
776
777 let mut previous = None;
778 let mut binding_active = false;
779 let loop_var_alloca = if should_bind {
780 let alloca = self.fresh_temp();
781 self.lines.push(format!(" {alloca} = alloca i64"));
782 previous = env.insert(
783 name.clone(),
784 LocalVar {
785 ptr: alloca.clone(),
786 ty: SimpleTy::I64,
787 used: false,
788 },
789 );
790 binding_active = true;
791 Some(alloca)
792 } else {
793 None
794 };
795
796 let head = self.fresh_label("for.head");
797 let body_label = self.fresh_label("for.body");
798 let step_label = self.fresh_label("for.step");
799 let end_label = self.fresh_label("for.end");
800
801 self.loop_stack.push(LoopLabels {
802 break_label: end_label.clone(),
803 continue_label: step_label.clone(),
804 });
805
806 self.lines.push(format!(" br label %{head}"));
807 self.lines.push(format!("{head}:"));
808 let idx_val = self.fresh_temp();
809 self.lines
810 .push(format!(" {idx_val} = load i64, i64* {idx_alloca}"));
811 let cmp = self.fresh_temp();
812 self.lines.push(format!(
813 " {cmp} = icmp slt i64 {idx_val}, {}",
814 end.llvm_value
815 ));
816 self.lines.push(format!(
817 " br i1 {cmp}, label %{body_label}, label %{end_label}"
818 ));
819
820 self.lines.push(format!("{body_label}:"));
821 if let Some(loop_var_alloca) = &loop_var_alloca {
822 self.lines
823 .push(format!(" store i64 {idx_val}, i64* {loop_var_alloca}"));
824 }
825 let body_flow = self.emit_block(body, env, ret_ty);
826 if !matches!(
827 body_flow,
828 ControlFlow::Break | ControlFlow::Continue | ControlFlow::Return
829 ) {
830 self.lines.push(format!(" br label %{step_label}"));
831 }
832
833 self.lines.push(format!("{step_label}:"));
834 let idx_next = self.fresh_temp();
835 self.lines
836 .push(format!(" {idx_next} = add i64 {idx_val}, 1"));
837 self.lines
838 .push(format!(" store i64 {idx_next}, i64* {idx_alloca}"));
839 self.lines.push(format!(" br label %{head}"));
840
841 self.lines.push(format!("{end_label}:"));
842 self.loop_stack.pop();
843 if binding_active {
844 if let Some(prev) = previous {
845 env.insert(name.clone(), prev);
846 } else {
847 env.remove(name);
848 }
849 }
850 self.used_vars = previous_used;
851 match body_flow {
852 ControlFlow::Return => ControlFlow::Return,
853 _ => ControlFlow::None,
854 }
855 }
856 Stmt::Break => {
857 if let Some(labels) = self.loop_stack.last() {
858 self.lines
859 .push(format!(" br label %{}", labels.break_label));
860 ControlFlow::Break
861 } else {
862 self.lines
865 .push(" ; break outside loop - unreachable".to_string());
866 ControlFlow::None
867 }
868 }
869 Stmt::Continue => {
870 if let Some(labels) = self.loop_stack.last() {
871 self.lines
872 .push(format!(" br label %{}", labels.continue_label));
873 ControlFlow::Continue
874 } else {
875 self.lines
878 .push(" ; continue outside loop - unreachable".to_string());
879 ControlFlow::None
880 }
881 }
882 Stmt::Expr(expr) => self.emit_expr_stmt(expr, env, ret_ty),
883 Stmt::Return(expr) => {
884 if let Some(expr) = expr {
885 let value = self.emit_expr(expr, env);
886 let llvm_ty = self.llvm_ty(value.ty);
887 self.lines
888 .push(format!(" ret {llvm_ty} {}", value.llvm_value));
889 } else {
890 match ret_ty {
891 SimpleTy::I64 => self.lines.push(" ret i64 0".to_string()),
892 SimpleTy::Bool => self.lines.push(" ret i1 0".to_string()),
893 SimpleTy::I64Ptr => self.lines.push(" ret i64* null".to_string()),
894 SimpleTy::Str => self.lines.push(" ret i8* null".to_string()),
895 _ => self.lines.push(" ret void".to_string()),
896 }
897 }
898 ControlFlow::Return
899 }
900 Stmt::Lambda { .. } => {
901 ControlFlow::None
903 }
904 }
905 }
906
907 fn emit_expr_stmt(
908 &mut self,
909 expr: &Expr,
910 env: &mut HashMap<String, LocalVar>,
911 ret_ty: SimpleTy,
912 ) -> ControlFlow {
913 match &expr.kind {
914 ExprKind::If {
915 condition,
916 then_branch,
917 else_branch,
918 } => self.emit_if_stmt(condition, then_branch, else_branch.as_ref(), env, ret_ty),
919 ExprKind::Match { expr, arms } => self.emit_match_stmt(expr, arms, env, ret_ty),
920 _ => {
921 self.emit_expr(expr, env);
922 ControlFlow::None
923 }
924 }
925 }
926
927 fn emit_block(
928 &mut self,
929 stmts: &[Stmt],
930 env: &mut HashMap<String, LocalVar>,
931 ret_ty: SimpleTy,
932 ) -> ControlFlow {
933 for stmt in stmts {
934 let flow = self.emit_stmt(stmt, env, ret_ty);
935 if flow != ControlFlow::None {
936 return flow;
937 }
938 }
939 ControlFlow::None
940 }
941
942 fn emit_if_stmt(
943 &mut self,
944 condition: &Expr,
945 then_branch: &[Stmt],
946 else_branch: Option<&tupa_parser::ElseBranch>,
947 env: &mut HashMap<String, LocalVar>,
948 ret_ty: SimpleTy,
949 ) -> ControlFlow {
950 let cond_val = self.emit_expr(condition, env);
951 let cond = if cond_val.ty == SimpleTy::Bool {
952 cond_val.llvm_value
953 } else {
954 "0".to_string()
955 };
956 let then_label = self.fresh_label("if.then");
957 let else_label = self.fresh_label("if.else");
958 let end_label = self.fresh_label("if.end");
959
960 self.lines.push(format!(
961 " br i1 {cond}, label %{then_label}, label %{else_label}"
962 ));
963 self.lines.push(format!("{then_label}:"));
964 let then_flow = self.emit_block(then_branch, env, ret_ty);
965 if then_flow == ControlFlow::None {
966 self.lines.push(format!(" br label %{end_label}"));
967 }
968 self.lines.push(format!("{else_label}:"));
969 let else_flow = match else_branch {
970 Some(tupa_parser::ElseBranch::Block(block)) => self.emit_block(block, env, ret_ty),
971 Some(tupa_parser::ElseBranch::If(expr)) => self.emit_expr_stmt(expr, env, ret_ty),
972 None => ControlFlow::None,
973 };
974 if else_flow == ControlFlow::None {
975 self.lines.push(format!(" br label %{end_label}"));
976 }
977 self.lines.push(format!("{end_label}:"));
978 match (then_flow, else_flow) {
979 (ControlFlow::Return, ControlFlow::Return) => ControlFlow::Return,
980 _ => ControlFlow::None,
981 }
982 }
983
984 fn emit_match_stmt(
985 &mut self,
986 scrutinee: &Expr,
987 arms: &[tupa_parser::MatchArm],
988 env: &mut HashMap<String, LocalVar>,
989 ret_ty: SimpleTy,
990 ) -> ControlFlow {
991 let value = self.emit_expr(scrutinee, env);
992 if value.ty != SimpleTy::I64 && value.ty != SimpleTy::Str && value.ty != SimpleTy::Bool {
993 return ControlFlow::None;
995 }
996 let end_label = self.fresh_label("match.end");
997 let mut arm_labels: Vec<String> = Vec::new();
998 let mut fallthrough_labels: Vec<String> = Vec::new();
999 let mut guard_labels: Vec<Option<String>> = Vec::new();
1000
1001 for _ in arms {
1002 arm_labels.push(self.fresh_label("match.arm"));
1003 fallthrough_labels.push(self.fresh_label("match.next"));
1004 guard_labels.push(None);
1005 }
1006
1007 for (idx, arm) in arms.iter().enumerate() {
1008 if arm.guard.is_some() {
1009 guard_labels[idx] = Some(self.fresh_label("match.guard"));
1010 }
1011 }
1012
1013 for (idx, arm) in arms.iter().enumerate() {
1014 let next_label = if idx + 1 < arms.len() {
1015 &fallthrough_labels[idx]
1016 } else {
1017 &end_label
1018 };
1019 let arm_target = guard_labels[idx].as_ref().unwrap_or(&arm_labels[idx]);
1020 let binding_name = match &arm.pattern {
1021 tupa_parser::Pattern::Ident(name) => Some(name.clone()),
1022 _ => None,
1023 };
1024 let should_bind = binding_name
1025 .as_ref()
1026 .map(|name| self.is_var_used(name))
1027 .unwrap_or(false);
1028 match (&arm.pattern, value.ty) {
1029 (tupa_parser::Pattern::Int(value_literal), SimpleTy::I64) => {
1030 let cmp = self.fresh_temp();
1031 self.lines.push(format!(
1032 " {cmp} = icmp eq i64 {}, {}",
1033 value.llvm_value, value_literal
1034 ));
1035 self.lines.push(format!(
1036 " br i1 {cmp}, label %{}, label %{}",
1037 arm_target, next_label
1038 ));
1039 }
1040 (tupa_parser::Pattern::Bool(value_literal), SimpleTy::Bool) => {
1041 let cmp = self.fresh_temp();
1042 let literal = if *value_literal { "1" } else { "0" };
1043 self.lines.push(format!(
1044 " {cmp} = icmp eq i1 {}, {}",
1045 value.llvm_value, literal
1046 ));
1047 self.lines.push(format!(
1048 " br i1 {cmp}, label %{}, label %{}",
1049 arm_target, next_label
1050 ));
1051 }
1052 (tupa_parser::Pattern::Str(lit), SimpleTy::Str) => {
1053 let (global, len) = self.intern_string(lit);
1054 let ptr = self.fresh_temp();
1055 self.lines.push(format!(
1056 " {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1057 ));
1058 self.declare_strcmp();
1059 let cmp = self.fresh_temp();
1060 self.lines.push(format!(
1061 " {cmp} = call i32 @strcmp(i8* {}, i8* {ptr})",
1062 value.llvm_value
1063 ));
1064 let is_eq = self.fresh_temp();
1065 self.lines.push(format!(" {is_eq} = icmp eq i32 {cmp}, 0"));
1066 self.lines.push(format!(
1067 " br i1 {is_eq}, label %{}, label %{}",
1068 arm_target, next_label
1069 ));
1070 }
1071 (tupa_parser::Pattern::Wildcard, _) | (tupa_parser::Pattern::Ident(_), _) => {
1072 self.lines.push(format!(" br label %{}", arm_target));
1073 }
1074 _ => {
1075 self.lines.push(format!(" br label %{}", next_label));
1076 }
1077 }
1078
1079 let mut prev_binding: Option<LocalVar> = None;
1080 let mut bound = false;
1081 let mut binding_active = false;
1082 if let Some(guard_label) = &guard_labels[idx] {
1083 self.lines.push(format!("{guard_label}:"));
1084 if let Some(name) = &binding_name {
1085 if should_bind {
1086 let llvm_ty = self.llvm_ty(value.ty);
1087 let alloca = self.fresh_temp();
1088 self.lines.push(format!(" {alloca} = alloca {llvm_ty}"));
1089 self.lines.push(format!(
1090 " store {llvm_ty} {}, {llvm_ty}* {alloca}",
1091 value.llvm_value
1092 ));
1093 prev_binding = env.insert(
1094 name.clone(),
1095 LocalVar {
1096 ptr: alloca,
1097 ty: value.ty,
1098 used: false,
1099 },
1100 );
1101 bound = true;
1102 binding_active = true;
1103 }
1104 }
1105 let guard_value = self.emit_expr(arm.guard.as_ref().unwrap(), env);
1106 let cond = if guard_value.ty == SimpleTy::Bool {
1107 guard_value.llvm_value
1108 } else {
1109 "0".to_string()
1110 };
1111 self.lines.push(format!(
1112 " br i1 {cond}, label %{}, label %{}",
1113 arm_labels[idx], next_label
1114 ));
1115 }
1116
1117 self.lines.push(format!("{}:", arm_labels[idx]));
1118 if !bound {
1119 if let Some(name) = &binding_name {
1120 if should_bind {
1121 let llvm_ty = self.llvm_ty(value.ty);
1122 let alloca = self.fresh_temp();
1123 self.lines.push(format!(" {alloca} = alloca {llvm_ty}"));
1124 self.lines.push(format!(
1125 " store {llvm_ty} {}, {llvm_ty}* {alloca}",
1126 value.llvm_value
1127 ));
1128 prev_binding = env.insert(
1129 name.clone(),
1130 LocalVar {
1131 ptr: alloca,
1132 ty: value.ty,
1133 used: false,
1134 },
1135 );
1136 binding_active = true;
1137 }
1138 }
1139 }
1140 let returned = match &arm.expr.kind {
1141 ExprKind::Block(stmts) => self.emit_block(stmts, env, ret_ty),
1142 _ => self.emit_expr_stmt(&arm.expr, env, ret_ty),
1143 };
1144 if returned == ControlFlow::None {
1145 self.lines.push(format!(" br label %{end_label}"));
1146 }
1147 if binding_active {
1148 if let Some(name) = &binding_name {
1149 if let Some(prev) = prev_binding.take() {
1150 env.insert(name.clone(), prev);
1151 } else {
1152 env.remove(name);
1153 }
1154 }
1155 }
1156 if idx + 1 < arms.len() {
1157 self.lines.push(format!("{}:", fallthrough_labels[idx]));
1158 }
1159 }
1160
1161 self.lines.push(format!("{end_label}:"));
1162 ControlFlow::None
1163 }
1164
1165 fn emit_block_expr(
1166 &mut self,
1167 stmts: &[Stmt],
1168 env: &mut HashMap<String, LocalVar>,
1169 ret_ty: SimpleTy,
1170 ) -> ExprValue {
1171 if stmts.is_empty() {
1172 return ExprValue::new(SimpleTy::Void, "0".to_string());
1173 }
1174 let last_index = stmts.len().saturating_sub(1);
1175 for (idx, stmt) in stmts.iter().enumerate() {
1176 if idx == last_index {
1177 if let Stmt::Expr(expr) = stmt {
1178 return self.emit_expr(expr, env);
1179 }
1180 self.emit_stmt(stmt, env, ret_ty);
1181 return ExprValue::new(SimpleTy::Void, "0".to_string());
1182 }
1183 if self.emit_stmt(stmt, env, ret_ty) == ControlFlow::Return {
1184 return ExprValue::new(SimpleTy::Void, "0".to_string());
1185 }
1186 }
1187 ExprValue::new(SimpleTy::Void, "0".to_string())
1188 }
1189
1190 fn infer_block_expr_ty(&self, stmts: &[Stmt], env: &HashMap<String, LocalVar>) -> SimpleTy {
1191 for stmt in stmts.iter().rev() {
1192 match stmt {
1193 Stmt::Expr(expr) => return self.infer_expr_ty(expr, env),
1194 Stmt::Return(Some(expr)) => return self.infer_expr_ty(expr, env),
1195 Stmt::Return(None) => return SimpleTy::Void,
1196 _ => continue,
1197 }
1198 }
1199 SimpleTy::Void
1200 }
1201
1202 fn infer_expr_ty(&self, expr: &Expr, env: &HashMap<String, LocalVar>) -> SimpleTy {
1203 match &expr.kind {
1204 ExprKind::Int(_) => SimpleTy::I64,
1205 ExprKind::Float(_) => SimpleTy::F64,
1206 ExprKind::Bool(_) => SimpleTy::Bool,
1207 ExprKind::Str(_) => SimpleTy::Str,
1208 ExprKind::ArrayLiteral(items) => {
1209 if items.is_empty() {
1210 return SimpleTy::I64Ptr;
1211 }
1212 let first = self.infer_expr_ty(&items[0], env);
1213 if first == SimpleTy::I64 {
1214 SimpleTy::I64Ptr
1215 } else if first == SimpleTy::F64 {
1216 SimpleTy::F64Ptr
1217 } else if first == SimpleTy::Str {
1218 SimpleTy::StrPtr
1219 } else if first == SimpleTy::Bool {
1220 SimpleTy::BoolPtr
1221 } else {
1222 SimpleTy::Unknown
1223 }
1224 }
1225 ExprKind::Tuple(_) => SimpleTy::Unknown,
1226 ExprKind::Ident(name) => env.get(name).map(|v| v.ty).unwrap_or(SimpleTy::Unknown),
1227 ExprKind::Assign { expr, .. } => self.infer_expr_ty(expr, env),
1228 ExprKind::AssignIndex { expr, .. } => match self.infer_expr_ty(expr, env) {
1229 SimpleTy::F64Ptr => SimpleTy::F64,
1230 SimpleTy::I64Ptr => SimpleTy::I64,
1231 SimpleTy::StrPtr => SimpleTy::Str,
1232 SimpleTy::BoolPtr => SimpleTy::Bool,
1233 _ => SimpleTy::Unknown,
1234 },
1235 ExprKind::Index { expr, .. } => match self.infer_expr_ty(expr, env) {
1236 SimpleTy::F64Ptr => SimpleTy::F64,
1237 SimpleTy::I64Ptr => SimpleTy::I64,
1238 SimpleTy::StrPtr => SimpleTy::Str,
1239 SimpleTy::BoolPtr => SimpleTy::Bool,
1240 _ => SimpleTy::Unknown,
1241 },
1242 ExprKind::Unary { op, expr } => {
1243 let inner = self.infer_expr_ty(expr, env);
1244 match (op, inner) {
1245 (tupa_parser::UnaryOp::Neg, SimpleTy::I64) => SimpleTy::I64,
1246 (tupa_parser::UnaryOp::Neg, SimpleTy::F64) => SimpleTy::F64,
1247 (tupa_parser::UnaryOp::Not, SimpleTy::Bool) => SimpleTy::Bool,
1248 _ => SimpleTy::Unknown,
1249 }
1250 }
1251 ExprKind::Binary { op, left, right } => {
1252 let l = self.infer_expr_ty(left, env);
1253 let r = self.infer_expr_ty(right, env);
1254 match (l, r) {
1255 (SimpleTy::I64, SimpleTy::I64) => match op {
1256 tupa_parser::BinaryOp::Equal
1257 | tupa_parser::BinaryOp::NotEqual
1258 | tupa_parser::BinaryOp::Less
1259 | tupa_parser::BinaryOp::LessEqual
1260 | tupa_parser::BinaryOp::Greater
1261 | tupa_parser::BinaryOp::GreaterEqual => SimpleTy::Bool,
1262 _ => SimpleTy::I64,
1263 },
1264 (SimpleTy::F64, SimpleTy::F64) => match op {
1265 tupa_parser::BinaryOp::Equal
1266 | tupa_parser::BinaryOp::NotEqual
1267 | tupa_parser::BinaryOp::Less
1268 | tupa_parser::BinaryOp::LessEqual
1269 | tupa_parser::BinaryOp::Greater
1270 | tupa_parser::BinaryOp::GreaterEqual => SimpleTy::Bool,
1271 _ => SimpleTy::F64,
1272 },
1273 (SimpleTy::Bool, SimpleTy::Bool) => match op {
1274 tupa_parser::BinaryOp::And
1275 | tupa_parser::BinaryOp::Or
1276 | tupa_parser::BinaryOp::Equal
1277 | tupa_parser::BinaryOp::NotEqual => SimpleTy::Bool,
1278 _ => SimpleTy::Unknown,
1279 },
1280 (SimpleTy::Str, SimpleTy::Str) => match op {
1281 tupa_parser::BinaryOp::Add => SimpleTy::Str,
1282 tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
1283 SimpleTy::Bool
1284 }
1285 _ => SimpleTy::Unknown,
1286 },
1287 (SimpleTy::Str, SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool)
1288 | (SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool, SimpleTy::Str)
1289 if matches!(op, tupa_parser::BinaryOp::Add) =>
1290 {
1291 SimpleTy::Str
1292 }
1293 _ => SimpleTy::Unknown,
1294 }
1295 }
1296 ExprKind::Call { callee, .. } => {
1297 if let ExprKind::Ident(name) = &callee.kind {
1298 if name == "print" {
1299 return SimpleTy::Void;
1300 }
1301 if let Some(sig) = self.function_sigs.get(name) {
1302 return sig.ret;
1303 }
1304 }
1305 SimpleTy::Unknown
1306 }
1307 ExprKind::If {
1308 then_branch,
1309 else_branch,
1310 ..
1311 } => match else_branch {
1312 Some(tupa_parser::ElseBranch::Block(block)) => {
1313 let then_ty = self.infer_block_expr_ty(then_branch, env);
1314 let else_ty = self.infer_block_expr_ty(block, env);
1315 if then_ty == else_ty {
1316 then_ty
1317 } else {
1318 SimpleTy::Unknown
1319 }
1320 }
1321 Some(tupa_parser::ElseBranch::If(expr)) => {
1322 let then_ty = self.infer_block_expr_ty(then_branch, env);
1323 let else_ty = self.infer_expr_ty(expr, env);
1324 if then_ty == else_ty {
1325 then_ty
1326 } else {
1327 SimpleTy::Unknown
1328 }
1329 }
1330 None => SimpleTy::Void,
1331 },
1332 ExprKind::Match { arms, .. } => {
1333 let mut expected: Option<SimpleTy> = None;
1334 for arm in arms {
1335 let arm_ty = match &arm.expr.kind {
1336 ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1337 _ => self.infer_expr_ty(&arm.expr, env),
1338 };
1339 match expected {
1340 None => expected = Some(arm_ty),
1341 Some(prev) if prev != arm_ty => return SimpleTy::Unknown,
1342 _ => {}
1343 }
1344 }
1345 expected.unwrap_or(SimpleTy::Void)
1346 }
1347 ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1348 _ => SimpleTy::Unknown,
1349 }
1350 }
1351
1352 fn emit_if_expr(
1353 &mut self,
1354 condition: &Expr,
1355 then_branch: &[Stmt],
1356 else_branch: Option<&tupa_parser::ElseBranch>,
1357 env: &mut HashMap<String, LocalVar>,
1358 ret_ty: SimpleTy,
1359 ) -> ExprValue {
1360 if else_branch.is_none() {
1361 self.emit_if_stmt(condition, then_branch, else_branch, env, ret_ty);
1362 return ExprValue::new(SimpleTy::Void, "0".to_string());
1363 }
1364 let result_ty = match else_branch {
1365 Some(tupa_parser::ElseBranch::Block(block)) => {
1366 let then_ty = self.infer_block_expr_ty(then_branch, env);
1367 let else_ty = self.infer_block_expr_ty(block, env);
1368 if then_ty == else_ty {
1369 then_ty
1370 } else {
1371 SimpleTy::Unknown
1372 }
1373 }
1374 Some(tupa_parser::ElseBranch::If(expr)) => {
1375 let then_ty = self.infer_block_expr_ty(then_branch, env);
1376 let else_ty = self.infer_expr_ty(expr, env);
1377 if then_ty == else_ty {
1378 then_ty
1379 } else {
1380 SimpleTy::Unknown
1381 }
1382 }
1383 None => SimpleTy::Void,
1384 };
1385 if matches!(result_ty, SimpleTy::Void | SimpleTy::Unknown) {
1386 self.emit_if_stmt(condition, then_branch, else_branch, env, ret_ty);
1387 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1388 }
1389
1390 let result_alloca = self.fresh_temp();
1391 let llvm_ty = self.llvm_ty(result_ty);
1392 self.lines
1393 .push(format!(" {result_alloca} = alloca {llvm_ty}"));
1394
1395 let cond_val = self.emit_expr(condition, env);
1396 let cond = if cond_val.ty == SimpleTy::Bool {
1397 cond_val.llvm_value
1398 } else {
1399 "0".to_string()
1400 };
1401 let then_label = self.fresh_label("if.then");
1402 let else_label = self.fresh_label("if.else");
1403 let end_label = self.fresh_label("if.end");
1404
1405 self.lines.push(format!(
1406 " br i1 {cond}, label %{then_label}, label %{else_label}"
1407 ));
1408 self.lines.push(format!("{then_label}:"));
1409 let then_val = self.emit_block_expr(then_branch, env, ret_ty);
1410 if then_val.ty == result_ty {
1411 self.lines.push(format!(
1412 " store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1413 then_val.llvm_value
1414 ));
1415 }
1416 self.lines.push(format!(" br label %{end_label}"));
1417
1418 self.lines.push(format!("{else_label}:"));
1419 let else_val = match else_branch {
1420 Some(tupa_parser::ElseBranch::Block(block)) => self.emit_block_expr(block, env, ret_ty),
1421 Some(tupa_parser::ElseBranch::If(expr)) => self.emit_expr(expr, env),
1422 None => ExprValue::new(SimpleTy::Void, "0".to_string()),
1423 };
1424 if else_val.ty == result_ty {
1425 self.lines.push(format!(
1426 " store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1427 else_val.llvm_value
1428 ));
1429 }
1430 self.lines.push(format!(" br label %{end_label}"));
1431 self.lines.push(format!("{end_label}:"));
1432 let out = self.fresh_temp();
1433 self.lines.push(format!(
1434 " {out} = load {llvm_ty}, {llvm_ty}* {result_alloca}"
1435 ));
1436 ExprValue::new(result_ty, out)
1437 }
1438
1439 fn emit_match_expr(
1440 &mut self,
1441 scrutinee: &Expr,
1442 arms: &[tupa_parser::MatchArm],
1443 env: &mut HashMap<String, LocalVar>,
1444 ret_ty: SimpleTy,
1445 ) -> ExprValue {
1446 let value = self.emit_expr(scrutinee, env);
1447 if value.ty != SimpleTy::I64 && value.ty != SimpleTy::Str && value.ty != SimpleTy::Bool {
1448 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1450 }
1451
1452 let mut result_ty: Option<SimpleTy> = None;
1453 for arm in arms {
1454 let arm_ty = match &arm.expr.kind {
1455 ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1456 _ => self.infer_expr_ty(&arm.expr, env),
1457 };
1458 match result_ty {
1459 None => result_ty = Some(arm_ty),
1460 Some(prev) if prev != arm_ty => {
1461 result_ty = Some(SimpleTy::Unknown);
1462 break;
1463 }
1464 _ => {}
1465 }
1466 }
1467 let result_ty = result_ty.unwrap_or(SimpleTy::Void);
1468 if matches!(result_ty, SimpleTy::Void | SimpleTy::Unknown) {
1469 self.emit_match_stmt(scrutinee, arms, env, ret_ty);
1470 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1471 }
1472
1473 let result_alloca = self.fresh_temp();
1474 let llvm_ty = self.llvm_ty(result_ty);
1475 self.lines
1476 .push(format!(" {result_alloca} = alloca {llvm_ty}"));
1477
1478 let end_label = self.fresh_label("match.end");
1479 let mut arm_labels: Vec<String> = Vec::new();
1480 let mut fallthrough_labels: Vec<String> = Vec::new();
1481 let mut guard_labels: Vec<Option<String>> = Vec::new();
1482
1483 for _ in arms {
1484 arm_labels.push(self.fresh_label("match.arm"));
1485 fallthrough_labels.push(self.fresh_label("match.next"));
1486 guard_labels.push(None);
1487 }
1488
1489 for (idx, arm) in arms.iter().enumerate() {
1490 if arm.guard.is_some() {
1491 guard_labels[idx] = Some(self.fresh_label("match.guard"));
1492 }
1493 }
1494
1495 for (idx, arm) in arms.iter().enumerate() {
1496 let next_label = if idx + 1 < arms.len() {
1497 &fallthrough_labels[idx]
1498 } else {
1499 &end_label
1500 };
1501 let arm_target = guard_labels[idx].as_ref().unwrap_or(&arm_labels[idx]);
1502 let binding_name = match &arm.pattern {
1503 tupa_parser::Pattern::Ident(name) => Some(name.clone()),
1504 _ => None,
1505 };
1506 let should_bind = binding_name
1507 .as_ref()
1508 .map(|name| self.is_var_used(name))
1509 .unwrap_or(false);
1510 match (&arm.pattern, value.ty) {
1511 (tupa_parser::Pattern::Int(value_literal), SimpleTy::I64) => {
1512 let cmp = self.fresh_temp();
1513 self.lines.push(format!(
1514 " {cmp} = icmp eq i64 {}, {}",
1515 value.llvm_value, value_literal
1516 ));
1517 self.lines.push(format!(
1518 " br i1 {cmp}, label %{}, label %{}",
1519 arm_target, next_label
1520 ));
1521 }
1522 (tupa_parser::Pattern::Bool(value_literal), SimpleTy::Bool) => {
1523 let cmp = self.fresh_temp();
1524 let literal = if *value_literal { "1" } else { "0" };
1525 self.lines.push(format!(
1526 " {cmp} = icmp eq i1 {}, {}",
1527 value.llvm_value, literal
1528 ));
1529 self.lines.push(format!(
1530 " br i1 {cmp}, label %{}, label %{}",
1531 arm_target, next_label
1532 ));
1533 }
1534 (tupa_parser::Pattern::Str(lit), SimpleTy::Str) => {
1535 let (global, len) = self.intern_string(lit);
1536 let ptr = self.fresh_temp();
1537 self.lines.push(format!(
1538 " {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1539 ));
1540 self.declare_strcmp();
1541 let cmp = self.fresh_temp();
1542 self.lines.push(format!(
1543 " {cmp} = call i32 @strcmp(i8* {}, i8* {ptr})",
1544 value.llvm_value
1545 ));
1546 let is_eq = self.fresh_temp();
1547 self.lines.push(format!(" {is_eq} = icmp eq i32 {cmp}, 0"));
1548 self.lines.push(format!(
1549 " br i1 {is_eq}, label %{}, label %{}",
1550 arm_target, next_label
1551 ));
1552 }
1553 (tupa_parser::Pattern::Wildcard, _) | (tupa_parser::Pattern::Ident(_), _) => {
1554 self.lines.push(format!(" br label %{}", arm_target));
1555 }
1556 _ => {
1557 self.lines.push(format!(" br label %{}", next_label));
1558 }
1559 }
1560
1561 let mut prev_binding: Option<LocalVar> = None;
1562 let mut bound = false;
1563 let mut binding_active = false;
1564 if let Some(guard_label) = &guard_labels[idx] {
1565 self.lines.push(format!("{guard_label}:"));
1566 if let Some(name) = &binding_name {
1567 if should_bind {
1568 let llvm_val_ty = self.llvm_ty(value.ty);
1569 let alloca = self.fresh_temp();
1570 self.lines
1571 .push(format!(" {alloca} = alloca {llvm_val_ty}"));
1572 self.lines.push(format!(
1573 " store {llvm_val_ty} {}, {llvm_val_ty}* {alloca}",
1574 value.llvm_value
1575 ));
1576 prev_binding = env.insert(
1577 name.clone(),
1578 LocalVar {
1579 ptr: alloca,
1580 ty: value.ty,
1581 used: false,
1582 },
1583 );
1584 bound = true;
1585 binding_active = true;
1586 }
1587 }
1588 let guard_value = self.emit_expr(arm.guard.as_ref().unwrap(), env);
1589 let cond = if guard_value.ty == SimpleTy::Bool {
1590 guard_value.llvm_value
1591 } else {
1592 "0".to_string()
1593 };
1594 self.lines.push(format!(
1595 " br i1 {cond}, label %{}, label %{}",
1596 arm_labels[idx], next_label
1597 ));
1598 }
1599
1600 self.lines.push(format!("{}:", arm_labels[idx]));
1601 if !bound {
1602 if let Some(name) = &binding_name {
1603 if should_bind {
1604 let llvm_val_ty = self.llvm_ty(value.ty);
1605 let alloca = self.fresh_temp();
1606 self.lines
1607 .push(format!(" {alloca} = alloca {llvm_val_ty}"));
1608 self.lines.push(format!(
1609 " store {llvm_val_ty} {}, {llvm_val_ty}* {alloca}",
1610 value.llvm_value
1611 ));
1612 prev_binding = env.insert(
1613 name.clone(),
1614 LocalVar {
1615 ptr: alloca,
1616 ty: value.ty,
1617 used: false,
1618 },
1619 );
1620 binding_active = true;
1621 }
1622 }
1623 }
1624 let arm_val = match &arm.expr.kind {
1625 ExprKind::Block(stmts) => self.emit_block_expr(stmts, env, ret_ty),
1626 _ => self.emit_expr(&arm.expr, env),
1627 };
1628 if arm_val.ty == result_ty {
1629 self.lines.push(format!(
1630 " store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1631 arm_val.llvm_value
1632 ));
1633 }
1634 self.lines.push(format!(" br label %{end_label}"));
1635
1636 if binding_active {
1637 if let Some(name) = &binding_name {
1638 if let Some(prev) = prev_binding.take() {
1639 env.insert(name.clone(), prev);
1640 } else {
1641 env.remove(name);
1642 }
1643 }
1644 }
1645 if idx + 1 < arms.len() {
1646 self.lines.push(format!("{}:", fallthrough_labels[idx]));
1647 }
1648 }
1649
1650 self.lines.push(format!("{end_label}:"));
1651 let out = self.fresh_temp();
1652 self.lines.push(format!(
1653 " {out} = load {llvm_ty}, {llvm_ty}* {result_alloca}"
1654 ));
1655 ExprValue::new(result_ty, out)
1656 }
1657
1658 fn emit_expr(&mut self, expr: &Expr, env: &mut HashMap<String, LocalVar>) -> ExprValue {
1659 let folded_expr = self.fold_expr(expr);
1661 match &folded_expr.kind {
1662 ExprKind::Int(value) => ExprValue::new(SimpleTy::I64, value.to_string()),
1663 ExprKind::Float(value) => ExprValue::new(SimpleTy::F64, value.to_string()),
1664 ExprKind::Bool(value) => {
1665 let literal = if *value { "1" } else { "0" };
1666 ExprValue::new(SimpleTy::Bool, literal.to_string())
1667 }
1668 ExprKind::Str(value) => {
1669 let (global, len) = self.intern_string(value);
1670 let ptr = self.fresh_temp();
1671 self.lines.push(format!(
1672 " {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1673 ));
1674 ExprValue::new(SimpleTy::Str, ptr)
1675 }
1676 ExprKind::Lambda { params, body } => {
1677 let lambda_name = format!("lambda_{}", self.temp);
1679 self.temp += 1;
1680
1681 let _env_struct_name = format!("env_{}", lambda_name);
1684
1685 let env_struct_name = format!("env_{}", lambda_name);
1687
1688 let mut used_env: HashMap<String, bool> =
1689 env.keys().map(|name| (name.clone(), false)).collect();
1690 self.collect_used_in_expr(body, &mut used_env);
1691 let mut captured_vars = Vec::new();
1692 for (var_name, var_info) in env.iter() {
1693 if params.contains(var_name) {
1694 continue;
1695 }
1696 if used_env.get(var_name).copied().unwrap_or(false) {
1697 captured_vars.push((var_name.clone(), var_info.ty));
1698 }
1699 }
1700
1701 let env_var_names = captured_vars
1702 .iter()
1703 .map(|(name, _)| name.clone())
1704 .collect::<Vec<_>>();
1705 let _env_var_types = captured_vars.iter().map(|(_, ty)| *ty).collect::<Vec<_>>();
1706
1707 if !captured_vars.is_empty() {
1708 self.globals.push(format!("%{} = type {{", env_struct_name));
1710 for (var_name, var_ty) in &captured_vars {
1711 self.globals
1712 .push(format!(" {} {}", self.llvm_ty(*var_ty), var_name));
1713 }
1714 self.globals.push("}".to_string());
1715 }
1716
1717 let mut lambda_codegen = Codegen {
1719 temp: self.temp,
1720 ..Default::default()
1721 };
1722 self.temp += 100;
1723
1724 let mut lambda_params = vec!["i8* %env".to_string()];
1726 for param in params.iter() {
1727 lambda_params.push(format!("i64 %{}", param));
1728 }
1729 let param_decls = lambda_params.join(", ");
1730
1731 lambda_codegen
1732 .lines
1733 .push(format!("define i64 @{lambda_name}({param_decls}) {{"));
1734 lambda_codegen.lines.push("entry:".to_string());
1735
1736 let mut lambda_env = HashMap::new();
1738 if !env_var_names.is_empty() {
1739 let env_struct_ptr = lambda_codegen.fresh_temp();
1741 lambda_codegen.lines.push(format!(
1742 " {env_struct_ptr} = bitcast i8* %env to %{}*",
1743 env_struct_name
1744 ));
1745
1746 for (i, (var_name, var_ty)) in captured_vars.iter().enumerate() {
1748 let var_ptr = lambda_codegen.fresh_temp();
1749 lambda_codegen.lines.push(format!(
1750 " {var_ptr} = getelementptr inbounds %{}, %{}* {env_struct_ptr}, i32 0, i32 {}",
1751 env_struct_name, env_struct_name, i
1752 ));
1753
1754 let var_value = lambda_codegen.fresh_temp();
1755 lambda_codegen.lines.push(format!(
1756 " {var_value} = load {}, {}* {var_ptr}",
1757 self.llvm_ty(*var_ty),
1758 self.llvm_ty(*var_ty)
1759 ));
1760
1761 let var_alloca = lambda_codegen.fresh_temp();
1762 lambda_codegen
1763 .lines
1764 .push(format!(" {var_alloca} = alloca {}", self.llvm_ty(*var_ty)));
1765 lambda_codegen.lines.push(format!(
1766 " store {} {var_value}, {}* {var_alloca}",
1767 self.llvm_ty(*var_ty),
1768 self.llvm_ty(*var_ty)
1769 ));
1770
1771 lambda_env.insert(
1772 var_name.clone(),
1773 LocalVar {
1774 ptr: var_alloca,
1775 ty: *var_ty,
1776 used: false,
1777 },
1778 );
1779 }
1780 }
1781
1782 for param in params {
1784 let alloca = lambda_codegen.fresh_temp();
1785 lambda_codegen
1786 .lines
1787 .push(format!(" {alloca} = alloca i64"));
1788 lambda_codegen
1789 .lines
1790 .push(format!(" store i64 %{param}, i64* {alloca}"));
1791 lambda_env.insert(
1792 param.clone(),
1793 LocalVar {
1794 ptr: alloca,
1795 ty: SimpleTy::I64,
1796 used: false,
1797 },
1798 );
1799 }
1800
1801 let result = lambda_codegen.emit_expr(body, &mut lambda_env);
1803 lambda_codegen
1804 .lines
1805 .push(format!(" ret i64 {}", result.llvm_value));
1806 lambda_codegen.lines.push("}".to_string());
1807
1808 self.globals.extend(lambda_codegen.lines);
1810
1811 if !env_var_names.is_empty() {
1813 self.ensure_malloc_declared();
1815 let env_size_ptr = self.fresh_temp();
1816 self.lines.push(format!(
1817 " {env_size_ptr} = getelementptr %{}, %{}* null, i32 1",
1818 env_struct_name, env_struct_name
1819 ));
1820 let env_size = self.fresh_temp();
1821 self.lines.push(format!(
1822 " {env_size} = ptrtoint %{}* {env_size_ptr} to i64",
1823 env_struct_name
1824 ));
1825 let env_mem = self.fresh_temp();
1826 self.lines
1827 .push(format!(" {env_mem} = call i8* @malloc(i64 {env_size})"));
1828
1829 let env_struct_ptr = self.fresh_temp();
1830 self.lines.push(format!(
1831 " {env_struct_ptr} = bitcast i8* {env_mem} to %{}*",
1832 env_struct_name
1833 ));
1834
1835 for (i, var_name) in env_var_names.iter().enumerate() {
1837 if let Some(var_info) = env.get(var_name) {
1838 let field_ptr = self.fresh_temp();
1839 self.lines.push(format!(
1840 " {field_ptr} = getelementptr inbounds %{}, %{}* {env_struct_ptr}, i32 0, i32 {}",
1841 env_struct_name, env_struct_name, i
1842 ));
1843
1844 let loaded = self.fresh_temp();
1845 self.lines.push(format!(
1846 " {loaded} = load {}, {}* {}",
1847 self.llvm_ty(var_info.ty),
1848 self.llvm_ty(var_info.ty),
1849 var_info.ptr
1850 ));
1851 self.lines.push(format!(
1852 " store {} {loaded}, {}* {field_ptr}",
1853 self.llvm_ty(var_info.ty),
1854 self.llvm_ty(var_info.ty)
1855 ));
1856 }
1857 }
1858
1859 let bitcast = self.fresh_temp();
1861 self.lines.push(format!(
1862 " {bitcast} = bitcast %{}* {env_struct_ptr} to i8*",
1863 env_struct_name
1864 ));
1865
1866 ExprValue::new(SimpleTy::ClosurePtr, bitcast)
1867 } else {
1868 let bitcast = self.fresh_temp();
1870 self.lines.push(format!(
1871 " {bitcast} = bitcast i64 (i8*, i64)* @{lambda_name} to i8*"
1872 ));
1873 ExprValue::new(SimpleTy::ClosurePtr, bitcast)
1874 }
1875 }
1876 ExprKind::ArrayLiteral(items) => {
1877 if items.is_empty() {
1878 return ExprValue::new(SimpleTy::I64Ptr, "null".to_string());
1880 }
1881 let mut values = Vec::new();
1882 for item in items {
1883 let value = self.emit_expr(item, env);
1884 values.push(value);
1885 }
1886 let elem_ty = values.first().map(|v| v.ty).unwrap_or(SimpleTy::Unknown);
1887 if !values.iter().all(|v| v.ty == elem_ty) {
1888 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1891 }
1892 let len = values.len();
1893 match elem_ty {
1894 SimpleTy::I64 => {
1895 let arr = self.fresh_temp();
1896 self.lines.push(format!(" {arr} = alloca [{len} x i64]"));
1897 for (idx, value) in values.into_iter().enumerate() {
1898 let elem_ptr = self.fresh_temp();
1899 self.lines.push(format!(
1900 " {elem_ptr} = getelementptr inbounds [{len} x i64], [{len} x i64]* {arr}, i64 0, i64 {idx}"
1901 ));
1902 self.lines
1903 .push(format!(" store i64 {}, i64* {elem_ptr}", value.llvm_value));
1904 }
1905 let data_ptr = self.fresh_temp();
1906 self.lines.push(format!(
1907 " {data_ptr} = getelementptr inbounds [{len} x i64], [{len} x i64]* {arr}, i64 0, i64 0"
1908 ));
1909 ExprValue::new(SimpleTy::I64Ptr, data_ptr)
1910 }
1911 SimpleTy::F64 => {
1912 let arr = self.fresh_temp();
1913 self.lines
1914 .push(format!(" {arr} = alloca [{len} x double]"));
1915 for (idx, value) in values.into_iter().enumerate() {
1916 let elem_ptr = self.fresh_temp();
1917 self.lines.push(format!(
1918 " {elem_ptr} = getelementptr inbounds [{len} x double], [{len} x double]* {arr}, i64 0, i64 {idx}"
1919 ));
1920 self.lines.push(format!(
1921 " store double {}, double* {elem_ptr}",
1922 value.llvm_value
1923 ));
1924 }
1925 let data_ptr = self.fresh_temp();
1926 self.lines.push(format!(
1927 " {data_ptr} = getelementptr inbounds [{len} x double], [{len} x double]* {arr}, i64 0, i64 0"
1928 ));
1929 ExprValue::new(SimpleTy::F64Ptr, data_ptr)
1930 }
1931 SimpleTy::Str => {
1932 let arr = self.fresh_temp();
1933 self.lines.push(format!(" {arr} = alloca [{len} x i8*]"));
1934 for (idx, value) in values.into_iter().enumerate() {
1935 let elem_ptr = self.fresh_temp();
1936 self.lines.push(format!(
1937 " {elem_ptr} = getelementptr inbounds [{len} x i8*], [{len} x i8*]* {arr}, i64 0, i64 {idx}"
1938 ));
1939 self.lines
1940 .push(format!(" store i8* {}, i8** {elem_ptr}", value.llvm_value));
1941 }
1942 let data_ptr = self.fresh_temp();
1943 self.lines.push(format!(
1944 " {data_ptr} = getelementptr inbounds [{len} x i8*], [{len} x i8*]* {arr}, i64 0, i64 0"
1945 ));
1946 ExprValue::new(SimpleTy::StrPtr, data_ptr)
1947 }
1948 SimpleTy::Bool => {
1949 let arr = self.fresh_temp();
1950 self.lines.push(format!(" {arr} = alloca [{len} x i1]"));
1951 for (idx, value) in values.into_iter().enumerate() {
1952 let elem_ptr = self.fresh_temp();
1953 self.lines.push(format!(
1954 " {elem_ptr} = getelementptr inbounds [{len} x i1], [{len} x i1]* {arr}, i64 0, i64 {idx}"
1955 ));
1956 self.lines
1957 .push(format!(" store i1 {}, i1* {elem_ptr}", value.llvm_value));
1958 }
1959 let data_ptr = self.fresh_temp();
1960 self.lines.push(format!(
1961 " {data_ptr} = getelementptr inbounds [{len} x i1], [{len} x i1]* {arr}, i64 0, i64 0"
1962 ));
1963 ExprValue::new(SimpleTy::BoolPtr, data_ptr)
1964 }
1965 _ => {
1966 ExprValue::new(SimpleTy::Unknown, "0".to_string())
1968 }
1969 }
1970 }
1971 ExprKind::Tuple(items) => {
1972 for item in items {
1973 self.emit_expr(item, env);
1974 }
1975 ExprValue::new(SimpleTy::Unknown, "0".to_string())
1976 }
1977 ExprKind::Ident(name) => {
1978 if let Some(var) = env.get_mut(name) {
1979 var.used = true;
1980 let llvm_ty = self.llvm_ty(var.ty);
1981 let tmp = self.fresh_temp();
1982 self.lines
1983 .push(format!(" {tmp} = load {llvm_ty}, {llvm_ty}* {}", var.ptr));
1984 return ExprValue::new(var.ty, tmp);
1985 }
1986 ExprValue::new(SimpleTy::Unknown, "0".to_string())
1987 }
1988 ExprKind::Assign { name, expr } => {
1989 let value = self.emit_expr(expr, env);
1990 if let Some(var) = env.get_mut(name) {
1991 var.used = true;
1992 let llvm_ty = self.llvm_ty(var.ty);
1993 self.lines.push(format!(
1994 " store {llvm_ty} {}, {llvm_ty}* {}",
1995 value.llvm_value, var.ptr
1996 ));
1997 ExprValue::new(var.ty, value.llvm_value)
1998 } else {
1999 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2001 }
2002 }
2003 ExprKind::AssignIndex { expr, index, value } => {
2004 let base = self.emit_expr(expr, env);
2005 let idx = self.emit_expr(index, env);
2006 let rhs = self.emit_expr(value, env);
2007 if idx.ty != SimpleTy::I64 {
2008 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2010 }
2011 match (base.ty, rhs.ty) {
2012 (SimpleTy::I64Ptr, SimpleTy::I64) => {
2013 let elem_ptr = self.fresh_temp();
2014 self.lines.push(format!(
2015 " {elem_ptr} = getelementptr inbounds i64, i64* {}, i64 {}",
2016 base.llvm_value, idx.llvm_value
2017 ));
2018 self.lines
2019 .push(format!(" store i64 {}, i64* {elem_ptr}", rhs.llvm_value));
2020 ExprValue::new(SimpleTy::I64, rhs.llvm_value)
2021 }
2022 (SimpleTy::F64Ptr, SimpleTy::F64) => {
2023 let elem_ptr = self.fresh_temp();
2024 self.lines.push(format!(
2025 " {elem_ptr} = getelementptr inbounds double, double* {}, i64 {}",
2026 base.llvm_value, idx.llvm_value
2027 ));
2028 self.lines.push(format!(
2029 " store double {}, double* {elem_ptr}",
2030 rhs.llvm_value
2031 ));
2032 ExprValue::new(SimpleTy::F64, rhs.llvm_value)
2033 }
2034 (SimpleTy::StrPtr, SimpleTy::Str) => {
2035 let elem_ptr = self.fresh_temp();
2036 self.lines.push(format!(
2037 " {elem_ptr} = getelementptr inbounds i8*, i8** {}, i64 {}",
2038 base.llvm_value, idx.llvm_value
2039 ));
2040 self.lines
2041 .push(format!(" store i8* {}, i8** {elem_ptr}", rhs.llvm_value));
2042 ExprValue::new(SimpleTy::Str, rhs.llvm_value)
2043 }
2044 (SimpleTy::BoolPtr, SimpleTy::Bool) => {
2045 let elem_ptr = self.fresh_temp();
2046 self.lines.push(format!(
2047 " {elem_ptr} = getelementptr inbounds i1, i1* {}, i64 {}",
2048 base.llvm_value, idx.llvm_value
2049 ));
2050 self.lines
2051 .push(format!(" store i1 {}, i1* {elem_ptr}", rhs.llvm_value));
2052 ExprValue::new(SimpleTy::Bool, rhs.llvm_value)
2053 }
2054 _ => {
2055 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2057 }
2058 }
2059 }
2060 ExprKind::Index { expr, index } => {
2061 let base = self.emit_expr(expr, env);
2062 let idx = self.emit_expr(index, env);
2063 if base.ty == SimpleTy::I64Ptr && idx.ty == SimpleTy::I64 {
2064 let elem_ptr = self.fresh_temp();
2065 self.lines.push(format!(
2066 " {elem_ptr} = getelementptr inbounds i64, i64* {}, i64 {}",
2067 base.llvm_value, idx.llvm_value
2068 ));
2069 let tmp = self.fresh_temp();
2070 self.lines
2071 .push(format!(" {tmp} = load i64, i64* {elem_ptr}"));
2072 return ExprValue::new(SimpleTy::I64, tmp);
2073 }
2074 if base.ty == SimpleTy::F64Ptr && idx.ty == SimpleTy::I64 {
2075 let elem_ptr = self.fresh_temp();
2076 self.lines.push(format!(
2077 " {elem_ptr} = getelementptr inbounds double, double* {}, i64 {}",
2078 base.llvm_value, idx.llvm_value
2079 ));
2080 let tmp = self.fresh_temp();
2081 self.lines
2082 .push(format!(" {tmp} = load double, double* {elem_ptr}"));
2083 return ExprValue::new(SimpleTy::F64, tmp);
2084 }
2085 if base.ty == SimpleTy::StrPtr && idx.ty == SimpleTy::I64 {
2086 let elem_ptr = self.fresh_temp();
2087 self.lines.push(format!(
2088 " {elem_ptr} = getelementptr inbounds i8*, i8** {}, i64 {}",
2089 base.llvm_value, idx.llvm_value
2090 ));
2091 let tmp = self.fresh_temp();
2092 self.lines
2093 .push(format!(" {tmp} = load i8*, i8** {elem_ptr}"));
2094 return ExprValue::new(SimpleTy::Str, tmp);
2095 }
2096 if base.ty == SimpleTy::BoolPtr && idx.ty == SimpleTy::I64 {
2097 let elem_ptr = self.fresh_temp();
2098 self.lines.push(format!(
2099 " {elem_ptr} = getelementptr inbounds i1, i1* {}, i64 {}",
2100 base.llvm_value, idx.llvm_value
2101 ));
2102 let tmp = self.fresh_temp();
2103 self.lines
2104 .push(format!(" {tmp} = load i1, i1* {elem_ptr}"));
2105 return ExprValue::new(SimpleTy::Bool, tmp);
2106 }
2107 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2109 }
2110 ExprKind::If {
2111 condition,
2112 then_branch,
2113 else_branch,
2114 } => self.emit_if_expr(
2115 condition,
2116 then_branch,
2117 else_branch.as_ref(),
2118 env,
2119 SimpleTy::Void,
2120 ),
2121 ExprKind::Match { expr, arms } => self.emit_match_expr(expr, arms, env, SimpleTy::Void),
2122 ExprKind::Unary { op, expr } => {
2123 let inner = self.emit_expr(expr, env);
2124 match (op, inner.ty) {
2125 (tupa_parser::UnaryOp::Neg, SimpleTy::I64) => {
2126 let tmp = self.fresh_temp();
2127 self.lines
2128 .push(format!(" {tmp} = sub i64 0, {}", inner.llvm_value));
2129 ExprValue::new(SimpleTy::I64, tmp)
2130 }
2131 (tupa_parser::UnaryOp::Neg, SimpleTy::F64) => {
2132 let tmp = self.fresh_temp();
2133 self.lines
2134 .push(format!(" {tmp} = fneg double {}", inner.llvm_value));
2135 ExprValue::new(SimpleTy::F64, tmp)
2136 }
2137 (tupa_parser::UnaryOp::Not, SimpleTy::Bool) => {
2138 let tmp = self.fresh_temp();
2139 self.lines
2140 .push(format!(" {tmp} = xor i1 {}, 1", inner.llvm_value));
2141 ExprValue::new(SimpleTy::Bool, tmp)
2142 }
2143 _ => {
2144 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2146 }
2147 }
2148 }
2149 ExprKind::Call { callee, args } => {
2150 if let ExprKind::Ident(name) = &callee.kind {
2152 if name == "print" {
2153 if let Some(arg) = args.first() {
2154 let val = self.emit_expr(arg, env);
2155 match val.ty {
2156 SimpleTy::Str => {
2157 self.declare_puts();
2158 self.lines
2159 .push(format!(" call i32 @puts(i8* {})", val.llvm_value));
2160 }
2161 SimpleTy::I64 => {
2162 self.declare_printf();
2163 let (fmt, len) = self.intern_format_int();
2164 let fmt_ptr = self.fresh_temp();
2165 self.lines.push(format!(" {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2166 self.lines.push(format!(
2167 " call i32 (i8*, ...) @printf(i8* {fmt_ptr}, i64 {})",
2168 val.llvm_value
2169 ));
2170 }
2171 SimpleTy::F64 => {
2172 self.declare_printf();
2173 let (fmt, len) = self.intern_format_float();
2174 let fmt_ptr = self.fresh_temp();
2175 self.lines.push(format!(" {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2176 self.lines.push(format!(
2177 " call i32 (i8*, ...) @printf(i8* {fmt_ptr}, double {})",
2178 val.llvm_value
2179 ));
2180 }
2181 SimpleTy::Bool => {
2182 self.declare_printf();
2183 let (fmt, len) = self.intern_format_int();
2184 let fmt_ptr = self.fresh_temp();
2185 self.lines.push(format!(" {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2186 let zext = self.fresh_temp();
2187 self.lines.push(format!(
2188 " {zext} = zext i1 {} to i64",
2189 val.llvm_value
2190 ));
2191 self.lines.push(format!(
2192 " call i32 (i8*, ...) @printf(i8* {fmt_ptr}, i64 {zext})"
2193 ));
2194 }
2195 _ => {
2196 self.lines
2197 .push(" ; print de tipo não suportado".to_string());
2198 }
2199 }
2200 }
2201 return ExprValue::new(SimpleTy::Void, "0".to_string());
2202 }
2203 }
2204 let (is_direct_fn, fn_name) = match &callee.kind {
2207 ExprKind::Ident(name) => {
2208 if self.function_sigs.contains_key(name) {
2209 (true, name.clone())
2210 } else {
2211 (false, String::new())
2212 }
2213 }
2214 _ => (false, String::new()),
2215 };
2216 let mut arg_values = Vec::new();
2217 for arg in args {
2218 arg_values.push(self.emit_expr(arg, env));
2219 }
2220 let args_llvm = arg_values
2221 .iter()
2222 .map(|v| match v.ty {
2223 SimpleTy::I64 => format!("i64 {}", v.llvm_value),
2224 SimpleTy::F64 => format!("double {}", v.llvm_value),
2225 SimpleTy::Bool => format!("i1 {}", v.llvm_value),
2226 SimpleTy::Str => format!("i8* {}", v.llvm_value),
2227 _ => format!("i64 {}", v.llvm_value),
2228 })
2229 .collect::<Vec<_>>()
2230 .join(", ");
2231 let tmp = self.fresh_temp();
2232 if is_direct_fn {
2233 self.lines
2234 .push(format!(" {tmp} = call i64 @{fn_name}({})", args_llvm));
2235 } else {
2236 let callee_val = self.emit_expr(callee, env);
2237 self.lines.push(format!(
2238 " {tmp} = call i64 {}({})",
2239 callee_val.llvm_value, args_llvm
2240 ));
2241 }
2242 ExprValue::new(SimpleTy::I64, tmp)
2243 }
2244 ExprKind::Binary { op, left, right } => {
2245 if matches!(op, tupa_parser::BinaryOp::Range) {
2246 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2248 }
2249 if matches!(op, tupa_parser::BinaryOp::Add) {
2250 if let (ExprKind::Str(lhs), ExprKind::Str(rhs)) = (&left.kind, &right.kind) {
2251 let combined = format!("{lhs}{rhs}");
2252 let (global, len) = self.intern_string(&combined);
2253 let ptr = self.fresh_temp();
2254 self.lines.push(format!(
2255 " {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
2256 ));
2257 return ExprValue::new(SimpleTy::Str, ptr);
2258 }
2259 }
2260 let left_val = self.emit_expr(left, env);
2261 let right_val = self.emit_expr(right, env);
2262 match (left_val.ty, right_val.ty) {
2263 (SimpleTy::I64, SimpleTy::I64) => {
2264 if matches!(op, tupa_parser::BinaryOp::Pow) {
2265 let result_alloca = self.fresh_temp();
2266 self.lines.push(format!(" {result_alloca} = alloca i64"));
2267 self.lines
2268 .push(format!(" store i64 1, i64* {result_alloca}"));
2269
2270 let exp_alloca = self.fresh_temp();
2271 self.lines.push(format!(" {exp_alloca} = alloca i64"));
2272 self.lines.push(format!(
2273 " store i64 {}, i64* {exp_alloca}",
2274 right_val.llvm_value
2275 ));
2276
2277 let head = self.fresh_label("pow.head");
2278 let body = self.fresh_label("pow.body");
2279 let end = self.fresh_label("pow.end");
2280
2281 self.lines.push(format!(" br label %{head}"));
2282 self.lines.push(format!("{head}:"));
2283 let exp_val = self.fresh_temp();
2284 self.lines
2285 .push(format!(" {exp_val} = load i64, i64* {exp_alloca}"));
2286 let cond = self.fresh_temp();
2287 self.lines
2288 .push(format!(" {cond} = icmp sgt i64 {exp_val}, 0"));
2289 self.lines
2290 .push(format!(" br i1 {cond}, label %{body}, label %{end}"));
2291
2292 self.lines.push(format!("{body}:"));
2293 let res_val = self.fresh_temp();
2294 self.lines
2295 .push(format!(" {res_val} = load i64, i64* {result_alloca}"));
2296 let mul = self.fresh_temp();
2297 self.lines.push(format!(
2298 " {mul} = mul i64 {res_val}, {}",
2299 left_val.llvm_value
2300 ));
2301 self.lines
2302 .push(format!(" store i64 {mul}, i64* {result_alloca}"));
2303 let next_exp = self.fresh_temp();
2304 self.lines
2305 .push(format!(" {next_exp} = sub i64 {exp_val}, 1"));
2306 self.lines
2307 .push(format!(" store i64 {next_exp}, i64* {exp_alloca}"));
2308 self.lines.push(format!(" br label %{head}"));
2309
2310 self.lines.push(format!("{end}:"));
2311 let out = self.fresh_temp();
2312 self.lines
2313 .push(format!(" {out} = load i64, i64* {result_alloca}"));
2314 return ExprValue::new(SimpleTy::I64, out);
2315 }
2316 let op = match op {
2317 tupa_parser::BinaryOp::Add => Some("add"),
2318 tupa_parser::BinaryOp::Sub => Some("sub"),
2319 tupa_parser::BinaryOp::Mul => Some("mul"),
2320 tupa_parser::BinaryOp::Div => Some("sdiv"),
2321 tupa_parser::BinaryOp::Equal => Some("icmp eq"),
2322 tupa_parser::BinaryOp::NotEqual => Some("icmp ne"),
2323 tupa_parser::BinaryOp::Less => Some("icmp slt"),
2324 tupa_parser::BinaryOp::LessEqual => Some("icmp sle"),
2325 tupa_parser::BinaryOp::Greater => Some("icmp sgt"),
2326 tupa_parser::BinaryOp::GreaterEqual => Some("icmp sge"),
2327 _ => None,
2328 };
2329 if let Some(op) = op {
2330 let tmp = self.fresh_temp();
2331 if op.starts_with("icmp") {
2332 self.lines.push(format!(
2333 " {tmp} = {op} i64 {}, {}",
2334 left_val.llvm_value, right_val.llvm_value
2335 ));
2336 return ExprValue::new(SimpleTy::Bool, tmp);
2337 }
2338 self.lines.push(format!(
2339 " {tmp} = {op} i64 {}, {}",
2340 left_val.llvm_value, right_val.llvm_value
2341 ));
2342 return ExprValue::new(SimpleTy::I64, tmp);
2343 }
2344 }
2345 (SimpleTy::F64, SimpleTy::F64) => {
2346 if matches!(op, tupa_parser::BinaryOp::Pow) {
2347 self.declare_powf();
2348 let tmp = self.fresh_temp();
2349 self.lines.push(format!(
2350 " {tmp} = call double @llvm.pow.f64(double {}, double {})",
2351 left_val.llvm_value, right_val.llvm_value
2352 ));
2353 return ExprValue::new(SimpleTy::F64, tmp);
2354 }
2355 let op = match op {
2356 tupa_parser::BinaryOp::Add => Some("fadd"),
2357 tupa_parser::BinaryOp::Sub => Some("fsub"),
2358 tupa_parser::BinaryOp::Mul => Some("fmul"),
2359 tupa_parser::BinaryOp::Div => Some("fdiv"),
2360 tupa_parser::BinaryOp::Equal => Some("fcmp oeq"),
2361 tupa_parser::BinaryOp::NotEqual => Some("fcmp one"),
2362 tupa_parser::BinaryOp::Less => Some("fcmp olt"),
2363 tupa_parser::BinaryOp::LessEqual => Some("fcmp ole"),
2364 tupa_parser::BinaryOp::Greater => Some("fcmp ogt"),
2365 tupa_parser::BinaryOp::GreaterEqual => Some("fcmp oge"),
2366 _ => None,
2367 };
2368 if let Some(op) = op {
2369 let tmp = self.fresh_temp();
2370 if op.starts_with("fcmp") {
2371 self.lines.push(format!(
2372 " {tmp} = {op} double {}, {}",
2373 left_val.llvm_value, right_val.llvm_value
2374 ));
2375 return ExprValue::new(SimpleTy::Bool, tmp);
2376 }
2377 self.lines.push(format!(
2378 " {tmp} = {op} double {}, {}",
2379 left_val.llvm_value, right_val.llvm_value
2380 ));
2381 return ExprValue::new(SimpleTy::F64, tmp);
2382 }
2383 }
2384 (SimpleTy::Bool, SimpleTy::Bool) => match op {
2385 tupa_parser::BinaryOp::And => {
2386 let result_alloca = self.fresh_temp();
2387 self.lines.push(format!(" {result_alloca} = alloca i1"));
2388 self.lines
2389 .push(format!(" store i1 0, i1* {result_alloca}"));
2390 let rhs_label = self.fresh_label("and.rhs");
2391 let end_label = self.fresh_label("and.end");
2392 self.lines.push(format!(
2393 " br i1 {}, label %{rhs_label}, label %{end_label}",
2394 left_val.llvm_value
2395 ));
2396 self.lines.push(format!("{rhs_label}:"));
2397 let rhs_val = self.emit_expr(right, env);
2398 if rhs_val.ty == SimpleTy::Bool {
2399 self.lines.push(format!(
2400 " store i1 {}, i1* {result_alloca}",
2401 rhs_val.llvm_value
2402 ));
2403 }
2404 self.lines.push(format!(" br label %{end_label}"));
2405 self.lines.push(format!("{end_label}:"));
2406 let out = self.fresh_temp();
2407 self.lines
2408 .push(format!(" {out} = load i1, i1* {result_alloca}"));
2409 return ExprValue::new(SimpleTy::Bool, out);
2410 }
2411 tupa_parser::BinaryOp::Or => {
2412 let result_alloca = self.fresh_temp();
2413 self.lines.push(format!(" {result_alloca} = alloca i1"));
2414 self.lines
2415 .push(format!(" store i1 1, i1* {result_alloca}"));
2416 let rhs_label = self.fresh_label("or.rhs");
2417 let end_label = self.fresh_label("or.end");
2418 self.lines.push(format!(
2419 " br i1 {}, label %{end_label}, label %{rhs_label}",
2420 left_val.llvm_value
2421 ));
2422 self.lines.push(format!("{rhs_label}:"));
2423 let rhs_val = self.emit_expr(right, env);
2424 if rhs_val.ty == SimpleTy::Bool {
2425 self.lines.push(format!(
2426 " store i1 {}, i1* {result_alloca}",
2427 rhs_val.llvm_value
2428 ));
2429 }
2430 self.lines.push(format!(" br label %{end_label}"));
2431 self.lines.push(format!("{end_label}:"));
2432 let out = self.fresh_temp();
2433 self.lines
2434 .push(format!(" {out} = load i1, i1* {result_alloca}"));
2435 return ExprValue::new(SimpleTy::Bool, out);
2436 }
2437 tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
2438 let op = if matches!(op, tupa_parser::BinaryOp::Equal) {
2439 "icmp eq"
2440 } else {
2441 "icmp ne"
2442 };
2443 let tmp = self.fresh_temp();
2444 self.lines.push(format!(
2445 " {tmp} = {op} i1 {}, {}",
2446 left_val.llvm_value, right_val.llvm_value
2447 ));
2448 return ExprValue::new(SimpleTy::Bool, tmp);
2449 }
2450 _ => {}
2451 },
2452 (SimpleTy::Str, SimpleTy::Str) => {
2453 match op {
2454 tupa_parser::BinaryOp::Add => {
2455 return self.emit_string_concat(left_val, right_val);
2456 }
2457 tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
2458 let op = if matches!(op, tupa_parser::BinaryOp::Equal) {
2459 "icmp eq"
2460 } else {
2461 "icmp ne"
2462 };
2463 self.declare_strcmp();
2464 let tmp = self.fresh_temp();
2465 self.lines.push(format!(
2466 " {tmp} = call i32 @strcmp(i8* {}, i8* {})",
2467 left_val.llvm_value, right_val.llvm_value
2468 ));
2469 let bool_tmp = self.fresh_temp();
2470 self.lines.push(format!(" {bool_tmp} = {op} i32 {tmp}, 0"));
2471 return ExprValue::new(SimpleTy::Bool, bool_tmp);
2472 }
2473 _ => {}
2474 }
2475 return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2477 }
2478 (SimpleTy::Str, SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool)
2479 if matches!(op, tupa_parser::BinaryOp::Add) =>
2480 {
2481 if let Some(rhs_str) = self.emit_format_value(right_val) {
2482 return self.emit_string_concat(left_val, rhs_str);
2483 }
2484 }
2485 (SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool, SimpleTy::Str)
2486 if matches!(op, tupa_parser::BinaryOp::Add) =>
2487 {
2488 if let Some(lhs_str) = self.emit_format_value(left_val) {
2489 return self.emit_string_concat(lhs_str, right_val);
2490 }
2491 }
2492 _ => {}
2493 }
2494 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2496 }
2497 _ => {
2498 ExprValue::new(SimpleTy::Unknown, "0".to_string())
2500 }
2501 }
2502 }
2503
2504 fn map_type(&self, ty: &Type) -> String {
2505 match ty {
2506 Type::Ident(name) if name == "i64" => "i64".to_string(),
2507 Type::Ident(name) if name == "f64" => "double".to_string(),
2508 Type::Ident(name) if name == "bool" => "i1".to_string(),
2509 Type::Ident(name) if name == "string" => "i8*".to_string(),
2510 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2511 "i64*".to_string()
2512 }
2513 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2514 "double*".to_string()
2515 }
2516 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2517 "i8**".to_string()
2518 }
2519 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2520 "i1*".to_string()
2521 }
2522 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2523 "i64*".to_string()
2524 }
2525 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2526 "double*".to_string()
2527 }
2528 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2529 "i8**".to_string()
2530 }
2531 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2532 "i1*".to_string()
2533 }
2534 Type::Tuple(_) => "void".to_string(),
2535 _ => "void".to_string(),
2536 }
2537 }
2538
2539 fn simple_ty_from_type(&self, ty: &Type) -> SimpleTy {
2540 match ty {
2541 Type::Ident(name) if name == "i64" => SimpleTy::I64,
2542 Type::Ident(name) if name == "f64" => SimpleTy::F64,
2543 Type::Ident(name) if name == "bool" => SimpleTy::Bool,
2544 Type::Ident(name) if name == "string" => SimpleTy::Str,
2545 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2546 SimpleTy::I64Ptr
2547 }
2548 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2549 SimpleTy::F64Ptr
2550 }
2551 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2552 SimpleTy::StrPtr
2553 }
2554 Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2555 SimpleTy::BoolPtr
2556 }
2557 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2558 SimpleTy::I64Ptr
2559 }
2560 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2561 SimpleTy::F64Ptr
2562 }
2563 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2564 SimpleTy::StrPtr
2565 }
2566 Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2567 SimpleTy::BoolPtr
2568 }
2569 Type::Tuple(_) => SimpleTy::Unknown,
2570 _ => SimpleTy::Unknown,
2571 }
2572 }
2573
2574 fn llvm_ty(&self, ty: SimpleTy) -> &'static str {
2575 match ty {
2576 SimpleTy::I64 => "i64",
2577 SimpleTy::F64 => "double",
2578 SimpleTy::Bool => "i1",
2579 SimpleTy::I64Ptr => "i64*",
2580 SimpleTy::F64Ptr => "double*",
2581 SimpleTy::Str => "i8*",
2582 SimpleTy::StrPtr => "i8**",
2583 SimpleTy::BoolPtr => "i1*",
2584 SimpleTy::Void => "void",
2585 SimpleTy::ClosurePtr => "i8*", SimpleTy::Unknown => "i64",
2587 }
2588 }
2589
2590 fn intern_string(&mut self, value: &str) -> (String, usize) {
2591 if let Some((name, len)) = self.string_pool.get(value) {
2592 return (name.clone(), *len);
2593 }
2594 let (escaped, len) = escape_llvm_string(value);
2595 let name = format!("@.str{}", self.string_pool.len());
2596 let literal =
2597 format!("{name} = private unnamed_addr constant [{len} x i8] c\"{escaped}\\00\"");
2598 self.globals.push(literal);
2599 self.string_pool
2600 .insert(value.to_string(), (name.clone(), len));
2601 (name, len)
2602 }
2603
2604 fn intern_format_int(&mut self) -> (String, usize) {
2605 if self.fmt_int_emitted {
2606 return ("@.fmt_int".to_string(), 5);
2607 }
2608 self.fmt_int_emitted = true;
2609 let literal = "@.fmt_int = private unnamed_addr constant [5 x i8] c\"%ld\\0A\\00\"";
2610 self.globals.push(literal.to_string());
2611 ("@.fmt_int".to_string(), 5)
2612 }
2613
2614 fn intern_format_float(&mut self) -> (String, usize) {
2615 if self.fmt_float_emitted {
2616 return ("@.fmt_float".to_string(), 4);
2617 }
2618 self.fmt_float_emitted = true;
2619 let literal = "@.fmt_float = private unnamed_addr constant [4 x i8] c\"%f\\0A\\00\"";
2620 self.globals.push(literal.to_string());
2621 ("@.fmt_float".to_string(), 4)
2622 }
2623
2624 fn declare_printf(&mut self) {
2625 if !self.printf_declared {
2626 self.globals
2627 .push("declare i32 @printf(i8*, ...)".to_string());
2628 self.printf_declared = true;
2629 }
2630 }
2631
2632 fn declare_puts(&mut self) {
2633 if !self.puts_declared {
2634 self.globals.push("declare i32 @puts(i8*)".to_string());
2635 self.puts_declared = true;
2636 }
2637 }
2638
2639 fn declare_strcmp(&mut self) {
2640 if !self.strcmp_declared {
2641 self.globals
2642 .push("declare i32 @strcmp(i8*, i8*)".to_string());
2643 self.strcmp_declared = true;
2644 }
2645 }
2646
2647 fn declare_strlen(&mut self) {
2648 if !self.strlen_declared {
2649 self.globals.push("declare i64 @strlen(i8*)".to_string());
2650 self.strlen_declared = true;
2651 }
2652 }
2653
2654 fn declare_malloc(&mut self) {
2655 if !self.malloc_declared {
2656 self.globals.push("declare i8* @malloc(i64)".to_string());
2657 self.malloc_declared = true;
2658 }
2659 }
2660
2661 fn ensure_malloc_declared(&mut self) {
2662 if !self.malloc_declared {
2663 self.globals.push("declare i8* @malloc(i64)".to_string());
2664 self.malloc_declared = true;
2665 }
2666 }
2667
2668 fn declare_strcpy(&mut self) {
2669 if !self.strcpy_declared {
2670 self.globals
2671 .push("declare i8* @strcpy(i8*, i8*)".to_string());
2672 self.strcpy_declared = true;
2673 }
2674 }
2675
2676 fn declare_strcat(&mut self) {
2677 if !self.strcat_declared {
2678 self.globals
2679 .push("declare i8* @strcat(i8*, i8*)".to_string());
2680 self.strcat_declared = true;
2681 }
2682 }
2683
2684 fn fresh_temp(&mut self) -> String {
2685 let name = format!("%t{}", self.temp);
2686 self.temp += 1;
2687 name
2688 }
2689
2690 fn fresh_label(&mut self, prefix: &str) -> String {
2691 let name = format!("{prefix}{}", self.label);
2692 self.label += 1;
2693 name
2694 }
2695
2696 fn extract_range(
2697 &mut self,
2698 iter: &Expr,
2699 env: &mut HashMap<String, LocalVar>,
2700 ) -> Option<(ExprValue, ExprValue)> {
2701 if let ExprKind::Binary { op, left, right } = &iter.kind {
2702 if *op != tupa_parser::BinaryOp::Range {
2703 return None;
2704 }
2705 let start = self.emit_expr(left, env);
2706 let end = self.emit_expr(right, env);
2707 if start.ty == SimpleTy::I64 && end.ty == SimpleTy::I64 {
2708 return Some((start, end));
2709 }
2710 }
2711 None
2712 }
2713
2714 fn finish(mut self) -> String {
2715 if !self.globals.is_empty() {
2716 self.globals.push(String::new());
2717 }
2718 self.globals.extend(self.lines);
2719 self.globals.join("\n")
2720 }
2721}
2722
2723struct ExprValue {
2724 ty: SimpleTy,
2725 llvm_value: String,
2726}
2727
2728#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2729enum ControlFlow {
2730 None,
2731 Break,
2732 Continue,
2733 Return,
2734}
2735
2736impl ExprValue {
2737 fn new(ty: SimpleTy, llvm_value: String) -> Self {
2738 Self { ty, llvm_value }
2739 }
2740}
2741
2742fn escape_llvm_string(value: &str) -> (String, usize) {
2743 let mut out = String::new();
2744 let mut len = 0usize;
2745 for &byte in value.as_bytes() {
2746 len += 1;
2747 match byte {
2748 b'\\' => out.push_str("\\5C"),
2749 b'"' => out.push_str("\\22"),
2750 b'\n' => out.push_str("\\0A"),
2751 b'\r' => out.push_str("\\0D"),
2752 b'\t' => out.push_str("\\09"),
2753 0x20..=0x7E => out.push(byte as char),
2754 _ => out.push_str(&format!("\\{:02X}", byte)),
2755 }
2756 }
2757 (out, len + 1)
2758}
2759
2760#[cfg(test)]
2761mod tests {
2762 use super::*;
2763 use tupa_lexer::Span;
2764 use tupa_parser::{BinaryOp, Expr, ExprKind, Function, Item, Param, Program, Stmt, Type};
2765
2766 #[test]
2767 fn test_empty_function_codegen() {
2768 let program = Program {
2769 items: vec![Item::Function(Function {
2770 attrs: vec![],
2771 external_spec: None,
2772 name: "main".to_string(),
2773 params: vec![],
2774 return_type: None,
2775 body: vec![],
2776 })],
2777 };
2778 let code = generate_stub(&program);
2779 assert!(code.contains("define void @main()"));
2780 assert!(code.contains("entry:"));
2781 assert!(code.contains("ret void"));
2782 }
2783
2784 #[test]
2785 fn test_function_with_return_codegen() {
2786 let program = Program {
2787 items: vec![Item::Function(Function {
2788 attrs: vec![],
2789 external_spec: None,
2790 name: "test".to_string(),
2791 params: vec![],
2792 return_type: Some(Type::Ident("i64".to_string())),
2793 body: vec![Stmt::Return(Some(Expr {
2794 kind: ExprKind::Int(42),
2795 span: Span { start: 0, end: 0 },
2796 }))],
2797 })],
2798 };
2799 let code = generate_stub(&program);
2800 assert!(code.contains("define i64 @test()"));
2801 assert!(code.contains("ret i64 42"));
2802 }
2803
2804 #[test]
2805 fn test_string_literal_codegen() {
2806 let program = Program {
2807 items: vec![Item::Function(Function {
2808 attrs: vec![],
2809 external_spec: None,
2810 name: "test".to_string(),
2811 params: vec![],
2812 return_type: None,
2813 body: vec![Stmt::Expr(Expr {
2814 kind: ExprKind::Str("hello".to_string()),
2815 span: Span { start: 0, end: 0 },
2816 })],
2817 })],
2818 };
2819 let code = generate_stub(&program);
2820 assert!(code.contains("@.str"));
2821 assert!(code.contains("hello"));
2822 }
2823
2824 #[test]
2825 fn test_f64_pow_codegen() {
2826 let program = Program {
2827 items: vec![Item::Function(Function {
2828 attrs: vec![],
2829 external_spec: None,
2830 name: "powf".to_string(),
2831 params: vec![Param {
2832 name: "x".to_string(),
2833 ty: Type::Ident("f64".to_string()),
2834 }],
2835 return_type: Some(Type::Ident("f64".to_string())),
2836 body: vec![Stmt::Return(Some(Expr {
2837 kind: ExprKind::Binary {
2838 op: BinaryOp::Pow,
2839 left: Box::new(Expr {
2840 kind: ExprKind::Ident("x".to_string()),
2841 span: Span { start: 0, end: 0 },
2842 }),
2843 right: Box::new(Expr {
2844 kind: ExprKind::Float(3.0),
2845 span: Span { start: 0, end: 0 },
2846 }),
2847 },
2848 span: Span { start: 0, end: 0 },
2849 }))],
2850 })],
2851 };
2852 let code = generate_stub(&program);
2853 assert!(code.contains("declare double @llvm.pow.f64(double, double)"));
2854 assert!(code.contains("call double @llvm.pow.f64"));
2855 }
2856
2857 #[test]
2858 fn test_f64_pow_folding() {
2859 let program = Program {
2860 items: vec![Item::Function(Function {
2861 attrs: vec![],
2862 external_spec: None,
2863 name: "powf_const".to_string(),
2864 params: vec![],
2865 return_type: Some(Type::Ident("f64".to_string())),
2866 body: vec![Stmt::Return(Some(Expr {
2867 kind: ExprKind::Binary {
2868 op: BinaryOp::Pow,
2869 left: Box::new(Expr {
2870 kind: ExprKind::Float(2.0),
2871 span: Span { start: 0, end: 0 },
2872 }),
2873 right: Box::new(Expr {
2874 kind: ExprKind::Float(3.0),
2875 span: Span { start: 0, end: 0 },
2876 }),
2877 },
2878 span: Span { start: 0, end: 0 },
2879 }))],
2880 })],
2881 };
2882 let code = generate_stub(&program);
2883 assert!(code.contains("ret double 8"));
2884 assert!(!code.contains("llvm.pow.f64"));
2885 }
2886
2887 #[test]
2888 fn test_i64_pow_folding() {
2889 let program = Program {
2890 items: vec![Item::Function(Function {
2891 attrs: vec![],
2892 external_spec: None,
2893 name: "powi_const".to_string(),
2894 params: vec![],
2895 return_type: Some(Type::Ident("i64".to_string())),
2896 body: vec![Stmt::Return(Some(Expr {
2897 kind: ExprKind::Binary {
2898 op: BinaryOp::Pow,
2899 left: Box::new(Expr {
2900 kind: ExprKind::Int(2),
2901 span: Span { start: 0, end: 0 },
2902 }),
2903 right: Box::new(Expr {
2904 kind: ExprKind::Int(3),
2905 span: Span { start: 0, end: 0 },
2906 }),
2907 },
2908 span: Span { start: 0, end: 0 },
2909 }))],
2910 })],
2911 };
2912 let code = generate_stub(&program);
2913 assert!(code.contains("ret i64 8"));
2914 }
2915
2916 #[test]
2917 fn test_lambda_captures_only_used_vars() {
2918 let program = Program {
2919 items: vec![Item::Function(Function {
2920 attrs: vec![],
2921 external_spec: None,
2922 name: "main".to_string(),
2923 params: vec![],
2924 return_type: None,
2925 body: vec![
2926 Stmt::Let {
2927 name: "x".to_string(),
2928 ty: None,
2929 expr: Expr {
2930 kind: ExprKind::Int(1),
2931 span: Span { start: 0, end: 0 },
2932 },
2933 },
2934 Stmt::Let {
2935 name: "y".to_string(),
2936 ty: None,
2937 expr: Expr {
2938 kind: ExprKind::Int(2),
2939 span: Span { start: 0, end: 0 },
2940 },
2941 },
2942 Stmt::Let {
2943 name: "f".to_string(),
2944 ty: None,
2945 expr: Expr {
2946 kind: ExprKind::Lambda {
2947 params: vec!["z".to_string()],
2948 body: Box::new(Expr {
2949 kind: ExprKind::Binary {
2950 op: BinaryOp::Add,
2951 left: Box::new(Expr {
2952 kind: ExprKind::Ident("x".to_string()),
2953 span: Span { start: 0, end: 0 },
2954 }),
2955 right: Box::new(Expr {
2956 kind: ExprKind::Ident("z".to_string()),
2957 span: Span { start: 0, end: 0 },
2958 }),
2959 },
2960 span: Span { start: 0, end: 0 },
2961 }),
2962 },
2963 span: Span { start: 0, end: 0 },
2964 },
2965 },
2966 ],
2967 })],
2968 };
2969 let code = generate_stub(&program);
2970 assert!(code.contains("%env_lambda_"));
2971 assert!(code.contains(" i64 x"));
2972 assert!(!code.contains(" i64 y"));
2973 }
2974}