Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Perform translation of function calls

author
Danielle Maywood
committer
nandi
date (Jul 26, 2026, 12:11 PM -0700) commit 5e69ae7b parent 34064946 change-id qqwknvtk
+393 -72
+234 -43
compiler-core/src/cranelift/mir.rs
··· 1 - use std::ops::Deref; 1 + use std::{collections::HashMap, ops::Deref}; 2 2 3 3 use ecow::{EcoString, eco_format}; 4 - use im::HashSet; 5 4 use num_bigint::BigInt; 6 5 use vec1::Vec1; 7 6 ··· 40 39 } 41 40 42 41 #[derive(Debug, PartialEq, Eq, Clone)] 42 + pub struct Var { 43 + pub name: EcoString, 44 + } 45 + 46 + #[derive(Debug, PartialEq, Eq, Clone)] 43 47 pub enum Expression { 44 48 Block(Vec<Expression>), 45 49 46 - Var { 50 + FunctionRef { 51 + module: EcoString, 47 52 name: EcoString, 48 53 }, 49 54 55 + Var(Var), 56 + 50 57 Int { 51 58 value: BigInt, 52 59 }, ··· 57 64 }, 58 65 59 66 IntAdd { 67 + lhs: Box<Expression>, 68 + rhs: Box<Expression>, 69 + }, 70 + 71 + IntSub { 60 72 lhs: Box<Expression>, 61 73 rhs: Box<Expression>, 62 74 }, 63 75 64 76 Set { 65 - name: EcoString, 77 + name: Var, 66 78 value: Box<Expression>, 67 79 }, 68 80 ··· 70 82 cond: Box<Expression>, 71 83 then: Box<Expression>, 72 84 else_: Box<Expression>, 85 + }, 86 + 87 + Call { 88 + target: Box<Expression>, 89 + args: Vec<Expression>, 73 90 }, 74 91 } 75 92 ··· 104 121 FunctionParameter { name, type_ } 105 122 })); 106 123 107 - let body = Self::flatten_expression(BodyTranslator::default().translate(&function.body)); 124 + let mut translator = BodyTranslator::default(); 125 + for argument in &function.arguments { 126 + if let Some(name) = argument.get_variable_name() { 127 + _ = translator.make_var(name.clone()); 128 + } 129 + } 130 + 131 + let body = translator.translate(&function.body); 132 + let body = Self::flatten_expression(body); 133 + let body = Self::remove_unused_code(body); 108 134 109 135 Function { 110 136 name, ··· 154 180 match expression { 155 181 Expression::Block(mut expressions) => block.append(&mut expressions), 156 182 Expression::Var { .. } 183 + | Expression::FunctionRef { .. } 157 184 | Expression::Int { .. } 158 185 | Expression::Equals { .. } 159 186 | Expression::IntAdd { .. } 187 + | Expression::IntSub { .. } 160 188 | Expression::Set { .. } 161 - | Expression::If { .. } => block.push(expression), 189 + | Expression::If { .. } 190 + | Expression::Call { .. } => block.push(expression), 162 191 }; 163 192 } 164 193 ··· 167 196 _ => Expression::Block(block), 168 197 } 169 198 } 170 - Expression::Var { name: _ } => expression, 171 - Expression::Int { value: _ } => expression, 199 + Expression::FunctionRef { module, name } => Expression::FunctionRef { module, name }, 200 + Expression::Var(var) => Expression::Var(var), 201 + Expression::Int { value } => Expression::Int { value }, 172 202 Expression::Equals { lhs, rhs } => Expression::Equals { 173 203 lhs: Box::new(Self::flatten_expression(*lhs)), 174 204 rhs: Box::new(Self::flatten_expression(*rhs)), 175 205 }, 176 206 Expression::IntAdd { lhs, rhs } => Expression::IntAdd { 207 + lhs: Box::new(Self::flatten_expression(*lhs)), 208 + rhs: Box::new(Self::flatten_expression(*rhs)), 209 + }, 210 + Expression::IntSub { lhs, rhs } => Expression::IntSub { 177 211 lhs: Box::new(Self::flatten_expression(*lhs)), 178 212 rhs: Box::new(Self::flatten_expression(*rhs)), 179 213 }, ··· 186 220 then: Box::new(Self::flatten_expression(*then)), 187 221 else_: Box::new(Self::flatten_expression(*else_)), 188 222 }, 223 + Expression::Call { target, args } => Expression::Call { 224 + target: Box::new(Self::flatten_expression(*target)), 225 + args: args.into_iter().map(Self::flatten_expression).collect(), 226 + }, 227 + } 228 + } 229 + 230 + fn remove_unused_code(expression: Expression) -> Expression { 231 + match expression { 232 + Expression::Block(expressions) => { 233 + let mut block = vec![]; 234 + 235 + let last_expression_index = expressions.len() - 1; 236 + 237 + for (index, expression) in expressions.into_iter().enumerate() { 238 + let expression = Self::remove_unused_code(expression); 239 + 240 + match expression { 241 + Expression::Var { .. } | Expression::FunctionRef { .. } 242 + if index != last_expression_index => {} 243 + 244 + Expression::Block(_) 245 + | Expression::FunctionRef { .. } 246 + | Expression::Var { .. } 247 + | Expression::Int { .. } 248 + | Expression::Equals { .. } 249 + | Expression::IntAdd { .. } 250 + | Expression::IntSub { .. } 251 + | Expression::Set { .. } 252 + | Expression::If { .. } 253 + | Expression::Call { .. } => block.push(expression), 254 + } 255 + } 256 + 257 + match block.as_slice() { 258 + [single] => single.clone(), 259 + _ => Expression::Block(block), 260 + } 261 + } 262 + Expression::FunctionRef { module, name } => Expression::FunctionRef { module, name }, 263 + Expression::Var(var) => Expression::Var(var), 264 + Expression::Int { value } => Expression::Int { value }, 265 + Expression::Equals { lhs, rhs } => Expression::Equals { 266 + lhs: Box::new(Self::remove_unused_code(*lhs)), 267 + rhs: Box::new(Self::remove_unused_code(*rhs)), 268 + }, 269 + Expression::IntAdd { lhs, rhs } => Expression::IntAdd { 270 + lhs: Box::new(Self::remove_unused_code(*lhs)), 271 + rhs: Box::new(Self::remove_unused_code(*rhs)), 272 + }, 273 + Expression::IntSub { lhs, rhs } => Expression::IntSub { 274 + lhs: Box::new(Self::remove_unused_code(*lhs)), 275 + rhs: Box::new(Self::remove_unused_code(*rhs)), 276 + }, 277 + Expression::Set { name, value } => Expression::Set { 278 + name, 279 + value: Box::new(Self::remove_unused_code(*value)), 280 + }, 281 + Expression::If { cond, then, else_ } => Expression::If { 282 + cond: Box::new(Self::remove_unused_code(*cond)), 283 + then: Box::new(Self::remove_unused_code(*then)), 284 + else_: Box::new(Self::remove_unused_code(*else_)), 285 + }, 286 + Expression::Call { target, args } => Expression::Call { 287 + target: Box::new(Self::remove_unused_code(*target)), 288 + args: args.into_iter().map(Self::remove_unused_code).collect(), 289 + }, 189 290 } 190 291 } 191 292 } 192 293 193 294 #[derive(Debug, Default)] 194 295 pub struct BodyTranslator { 195 - variables: HashSet<EcoString>, 296 + variables: HashMap<EcoString, EcoString>, 196 297 variable_count: usize, 197 298 } 198 299 ··· 203 304 } 204 305 205 306 impl BodyTranslator { 307 + fn make_tmp_var(&mut self) -> Var { 308 + self.make_var("_tmp".into()) 309 + } 310 + 311 + fn make_var(&mut self, name: EcoString) -> Var { 312 + self.variable_count += 1; 313 + 314 + let entry = eco_format!("{}${}", name.clone(), self.variable_count); 315 + _ = self.variables.insert(name.clone(), entry.clone()); 316 + 317 + Var { name: entry } 318 + } 319 + 320 + fn get_var(&mut self, name: &EcoString) -> Var { 321 + let variable = self 322 + .variables 323 + .get(name) 324 + .cloned() 325 + .unwrap_or_else(|| panic!("variable '{name}' not found")); 326 + 327 + Var { name: variable } 328 + } 329 + 330 + fn in_var_scope<T>(&mut self, func: impl Fn(&mut Self) -> T) -> T { 331 + let snapshot = self.variables.clone(); 332 + let result = func(self); 333 + self.variables = snapshot; 334 + result 335 + } 336 + 206 337 fn translate(mut self, body: &Vec1<ast::TypedStatement>) -> Expression { 338 + self.translate_statements(body) 339 + } 340 + 341 + fn translate_statements(&mut self, statements: &[ast::TypedStatement]) -> Expression { 207 342 Expression::Block(Iterator::collect( 208 - body.iter().map(|stmt| self.translate_statement(stmt)), 343 + statements.iter().map(|stmt| self.translate_statement(stmt)), 209 344 )) 210 345 } 211 346 ··· 240 375 } => todo!(), 241 376 ast::TypedExpr::Block { 242 377 location: _, 243 - statements: _, 244 - } => todo!(), 378 + statements, 379 + } => self.in_var_scope(|this| this.translate_statements(statements)), 245 380 ast::TypedExpr::Pipeline { 246 381 location: _, 247 382 first_value: _, ··· 251 386 } => todo!(), 252 387 ast::TypedExpr::Var { 253 388 location: _, 254 - constructor: _, 389 + constructor, 255 390 name, 256 - } => Expression::Var { name: name.clone() }, 391 + } => match &constructor.variant { 392 + type_::ValueConstructorVariant::LocalVariable { 393 + location: _, 394 + origin: _, 395 + } => Expression::Var(self.get_var(name)), 396 + type_::ValueConstructorVariant::ModuleConstant { 397 + documentation: _, 398 + location: _, 399 + module: _, 400 + name: _, 401 + literal: _, 402 + implementations: _, 403 + } => todo!(), 404 + type_::ValueConstructorVariant::LocalConstant { literal: _ } => todo!(), 405 + type_::ValueConstructorVariant::ModuleFn { 406 + name, 407 + field_map: _, 408 + module, 409 + arity: _, 410 + location: _, 411 + documentation: _, 412 + implementations: _, 413 + external_erlang: _, 414 + external_javascript: _, 415 + external_cranelift: _, 416 + purity: _, 417 + } => Expression::FunctionRef { 418 + module: module.clone(), 419 + name: name.clone(), 420 + }, 421 + type_::ValueConstructorVariant::Record { 422 + name: _, 423 + arity: _, 424 + field_map: _, 425 + location: _, 426 + module: _, 427 + variants_count: _, 428 + variant_index: _, 429 + documentation: _, 430 + } => todo!(), 431 + }, 257 432 ast::TypedExpr::Fn { 258 433 location: _, 259 434 type_: _, ··· 272 447 ast::TypedExpr::Call { 273 448 location: _, 274 449 type_: _, 275 - fun: _, 276 - arguments: _, 277 - } => todo!(), 450 + fun, 451 + arguments, 452 + } => { 453 + let target = self.translate_expression(fun); 454 + let args = arguments 455 + .iter() 456 + .map(|arg| self.translate_expression(&arg.value)) 457 + .collect(); 458 + 459 + Expression::Call { 460 + target: Box::new(target), 461 + args, 462 + } 463 + } 278 464 ast::TypedExpr::BinOp { 279 465 location: _, 280 466 type_: _, ··· 301 487 ast::BinOp::GtFloat => todo!(), 302 488 ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs }, 303 489 ast::BinOp::AddFloat => todo!(), 304 - ast::BinOp::SubInt => todo!(), 490 + ast::BinOp::SubInt => Expression::IntSub { lhs, rhs }, 305 491 ast::BinOp::SubFloat => todo!(), 306 492 ast::BinOp::MultInt => todo!(), 307 493 ast::BinOp::MultFloat => todo!(), ··· 431 617 &assignment.compiled_case.tree, 432 618 )); 433 619 434 - block.push(Expression::Var { name: value_var }); 620 + block.push(Expression::Var(value_var)); 435 621 436 622 Expression::Block(block) 437 623 } 438 624 439 - fn make_tmp_var(&mut self) -> EcoString { 440 - let variable_count = self.variable_count; 441 - self.make_var(eco_format!("_tmp{variable_count}")) 442 - } 443 - 444 - fn make_var(&mut self, name: EcoString) -> EcoString { 445 - self.variable_count += 1; 446 - _ = self.variables.insert(name.clone()); 447 - name 448 - } 449 - 450 625 fn translate_decision( 451 626 &mut self, 452 627 kind: DecisionKind, 453 - subjects: &[EcoString], 628 + subjects: &[Var], 454 629 clauses: &[ast::TypedClause], 455 630 decision: &exhaustiveness::Decision, 456 631 ) -> Expression { ··· 459 634 let mut block = vec![]; 460 635 461 636 for (binding, value) in &body.bindings { 637 + let binding = self.make_var(binding.clone()); 638 + 462 639 block.push(self.translate_binding(subjects, binding.clone(), value)); 463 640 } 464 641 ··· 488 665 .partition(|(binding, _)| referenced_in_guard.contains(binding)); 489 666 490 667 for (binding, value) in guard_bindings { 491 - block.push(self.translate_binding(subjects, binding.clone(), value)); 668 + let binding = self.make_var(binding.clone()); 669 + 670 + block.push(self.translate_binding(subjects, binding, value)); 492 671 } 493 672 494 673 let cond = self.translate_guard(guard); ··· 497 676 let mut block = vec![]; 498 677 499 678 for (binding, value) in if_true_bindings { 679 + let binding = self.make_var(binding.clone()); 680 + 500 681 block.push(self.translate_binding(subjects, binding.clone(), value)); 501 682 } 502 683 ··· 526 707 Expression::If { 527 708 cond: Box::new(self.translate_runtime_check( 528 709 check, 529 - Expression::Var { 530 - name: subjects[var.id].clone(), 531 - }, 710 + Expression::Var(subjects[var.id].clone()), 532 711 )), 533 712 then: Box::new(self.translate_decision(kind, subjects, clauses, then)), 534 713 else_: Box::new(fallback), ··· 667 846 type_: _, 668 847 name, 669 848 definition_location: _, 670 - } => Expression::Var { name: name.clone() }, 849 + } => Expression::Var(self.get_var(name)), 671 850 ast::ClauseGuard::TupleIndex { 672 851 location: _, 673 852 index: _, ··· 754 933 755 934 fn translate_binding( 756 935 &mut self, 757 - subjects: &[EcoString], 758 - binding: EcoString, 936 + subjects: &[Var], 937 + binding: Var, 759 938 value: &exhaustiveness::BoundValue, 760 939 ) -> Expression { 761 940 let value = match value { 762 - exhaustiveness::BoundValue::Variable(variable) => Expression::Var { 763 - name: subjects[variable.id].clone(), 764 - }, 941 + exhaustiveness::BoundValue::Variable(variable) => { 942 + Expression::Var(subjects[variable.id].clone()) 943 + } 765 944 exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(), 766 945 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int { 767 946 value: big_int.clone(), ··· 815 994 816 995 #[cfg(test)] 817 996 mod tests { 997 + use std::collections::{HashMap, HashSet}; 998 + 818 999 use super::*; 819 1000 use crate::analyse::TargetSupport; 820 1001 use crate::build::{Origin, Target}; ··· 844 1025 origin: Origin::Src, 845 1026 importable_modules: &modules, 846 1027 warnings: &TypeWarningEmitter::null(), 847 - direct_dependencies: &std::collections::HashMap::new(), 848 - dev_dependencies: &std::collections::HashSet::new(), 1028 + direct_dependencies: &HashMap::new(), 1029 + dev_dependencies: &HashSet::new(), 849 1030 target_support: TargetSupport::Enforced, 850 1031 package_config: &config, 851 1032 } ··· 883 1064 y if y == 0 -> 2 884 1065 1 -> 4 885 1066 _ -> 6 1067 + } 1068 + }"# 1069 + )); 1070 + 1071 + insta::assert_debug_snapshot!(translate( 1072 + r#"pub fn fib(n: Int) -> Int { 1073 + case n { 1074 + 0 -> 0 1075 + 1 -> 1 1076 + _ -> fib(n - 1) + fib(n - 2) 886 1077 } 887 1078 }"# 888 1079 ));
+17 -9
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples-2.snap
··· 18 18 body: Block( 19 19 [ 20 20 Set { 21 - name: "_tmp0", 22 - value: Var { 23 - name: "n", 21 + name: Var { 22 + name: "_tmp$2", 24 23 }, 24 + value: Var( 25 + Var { 26 + name: "n$1", 27 + }, 28 + ), 25 29 }, 26 30 If { 27 31 cond: Equals { 28 - lhs: Var { 29 - name: "_tmp0", 30 - }, 32 + lhs: Var( 33 + Var { 34 + name: "_tmp$2", 35 + }, 36 + ), 31 37 rhs: Int { 32 38 value: 0, 33 39 }, ··· 37 43 }, 38 44 else_: If { 39 45 cond: Equals { 40 - lhs: Var { 41 - name: "_tmp0", 42 - }, 46 + lhs: Var( 47 + Var { 48 + name: "_tmp$2", 49 + }, 50 + ), 43 51 rhs: Int { 44 52 value: 1, 45 53 },
+24 -12
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples-3.snap
··· 18 18 body: Block( 19 19 [ 20 20 Set { 21 - name: "_tmp0", 22 - value: Var { 23 - name: "n", 21 + name: Var { 22 + name: "_tmp$2", 24 23 }, 24 + value: Var( 25 + Var { 26 + name: "n$1", 27 + }, 28 + ), 25 29 }, 26 30 Set { 27 - name: "y", 28 - value: Var { 29 - name: "_tmp0", 31 + name: Var { 32 + name: "y$3", 30 33 }, 34 + value: Var( 35 + Var { 36 + name: "_tmp$2", 37 + }, 38 + ), 31 39 }, 32 40 If { 33 41 cond: Equals { 34 - lhs: Var { 35 - name: "y", 36 - }, 42 + lhs: Var( 43 + Var { 44 + name: "y$3", 45 + }, 46 + ), 37 47 rhs: Int { 38 48 value: 0, 39 49 }, ··· 43 53 }, 44 54 else_: If { 45 55 cond: Equals { 46 - lhs: Var { 47 - name: "_tmp0", 48 - }, 56 + lhs: Var( 57 + Var { 58 + name: "_tmp$2", 59 + }, 60 + ), 49 61 rhs: Int { 50 62 value: 1, 51 63 },
+102
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples-4.snap
··· 1 + --- 2 + source: compiler-core/src/cranelift/mir.rs 3 + expression: "translate(r#\"pub fn fib(n: Int) -> Int {\n case n {\n 0 -> 0\n 1 -> 1\n _ -> fib(n - 1) + fib(n - 2)\n }\n }\"#)" 4 + --- 5 + Module { 6 + functions: [ 7 + Function { 8 + name: "fib", 9 + return_type: Int, 10 + parameters: [ 11 + FunctionParameter { 12 + type_: Int, 13 + name: Some( 14 + "n", 15 + ), 16 + }, 17 + ], 18 + body: Block( 19 + [ 20 + Set { 21 + name: Var { 22 + name: "_tmp$2", 23 + }, 24 + value: Var( 25 + Var { 26 + name: "n$1", 27 + }, 28 + ), 29 + }, 30 + If { 31 + cond: Equals { 32 + lhs: Var( 33 + Var { 34 + name: "_tmp$2", 35 + }, 36 + ), 37 + rhs: Int { 38 + value: 0, 39 + }, 40 + }, 41 + then: Int { 42 + value: 0, 43 + }, 44 + else_: If { 45 + cond: Equals { 46 + lhs: Var( 47 + Var { 48 + name: "_tmp$2", 49 + }, 50 + ), 51 + rhs: Int { 52 + value: 1, 53 + }, 54 + }, 55 + then: Int { 56 + value: 1, 57 + }, 58 + else_: IntAdd { 59 + lhs: Call { 60 + target: FunctionRef { 61 + module: "", 62 + name: "fib", 63 + }, 64 + args: [ 65 + IntSub { 66 + lhs: Var( 67 + Var { 68 + name: "n$1", 69 + }, 70 + ), 71 + rhs: Int { 72 + value: 1, 73 + }, 74 + }, 75 + ], 76 + }, 77 + rhs: Call { 78 + target: FunctionRef { 79 + module: "", 80 + name: "fib", 81 + }, 82 + args: [ 83 + IntSub { 84 + lhs: Var( 85 + Var { 86 + name: "n$1", 87 + }, 88 + ), 89 + rhs: Int { 90 + value: 2, 91 + }, 92 + }, 93 + ], 94 + }, 95 + }, 96 + }, 97 + }, 98 + ], 99 + ), 100 + }, 101 + ], 102 + }
+10 -6
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples.snap
··· 22 22 }, 23 23 ], 24 24 body: IntAdd { 25 - lhs: Var { 26 - name: "lhs", 27 - }, 28 - rhs: Var { 29 - name: "rhs", 30 - }, 25 + lhs: Var( 26 + Var { 27 + name: "lhs$1", 28 + }, 29 + ), 30 + rhs: Var( 31 + Var { 32 + name: "rhs$2", 33 + }, 34 + ), 31 35 }, 32 36 }, 33 37 ],
+6 -2
test/project_cranelift/src/project_cranelift.gleam
··· 1 - pub fn add(lhs: Int, rhs: Int) -> Int { 2 - let x = 10 1 + pub fn fib(n: Int) -> Int { 2 + case n { 3 + 0 -> 0 4 + 1 -> 1 5 + _ -> fib(n - 1) + fib(n - 2) 6 + } 3 7 }