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