1use lamina_platform::{PointerWidth, SourceLoc};
6
7use crate::{
8 block::Block,
9 instruction::{Instruction, Operand},
10 register::Register,
11 register::VirtualReg,
12 types::{Aggregate, MirType, Signedness},
13 verify::verify_function,
14};
15use std::collections::HashMap;
16use std::fmt;
17
18#[derive(Debug, Clone, PartialEq)]
20pub struct Parameter {
21 pub reg: Register,
23
24 pub ty: MirType,
26
27 pub signedness: Signedness,
29
30 pub aggregate: Option<Aggregate>,
33
34 pub name: Option<String>,
37
38 pub byval_claimed: bool,
42}
43
44impl Parameter {
45 pub fn new(reg: Register, ty: MirType) -> Self {
48 Self {
49 reg,
50 ty,
51 signedness: Signedness::Signed,
52 aggregate: None,
53 name: None,
54 byval_claimed: false,
55 }
56 }
57
58 pub fn with_signedness(mut self, signedness: Signedness) -> Self {
59 self.signedness = signedness;
60 self
61 }
62
63 pub fn with_aggregate(mut self, aggregate: Aggregate) -> Self {
64 self.aggregate = Some(aggregate);
65 self
66 }
67
68 pub fn with_name(mut self, name: impl Into<String>) -> Self {
69 self.name = Some(name.into());
70 self
71 }
72
73 pub fn with_byval_claim(mut self, claimed: bool) -> Self {
74 self.byval_claimed = claimed;
75 self
76 }
77}
78
79#[derive(Debug, Clone, Default, PartialEq, Eq)]
83pub struct KernelMetadata {
84 pub required_workgroup_size: Option<[u32; 3]>,
85 pub max_threads: Option<u32>,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum SymbolVisibility {
91 Internal,
92 Private,
93 Hidden,
94 Protected,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum CodePlacement {
100 Cold,
101 Hot,
102}
103
104#[derive(Debug, Clone, PartialEq)]
106pub struct Signature {
107 pub name: String,
109
110 pub params: Vec<Parameter>,
112
113 pub ret_ty: Option<MirType>,
115
116 pub ret_signedness: Signedness,
118
119 pub ret_aggregate: Option<Aggregate>,
122
123 pub sret_claimed: bool,
127
128 pub kernel: Option<KernelMetadata>,
129
130 pub no_inline: bool,
132
133 pub inline_hint: bool,
135
136 pub noreturn: bool,
138
139 pub pure: bool,
141
142 pub visibility: Option<SymbolVisibility>,
144
145 pub placement: Option<CodePlacement>,
147
148 pub weak: bool,
150
151 pub section: Option<String>,
153
154 pub align: Option<u32>,
156
157 pub naked: bool,
159
160 pub variadic: bool,
163
164 pub decl: Option<SourceLoc>,
166}
167
168impl Signature {
169 pub fn new(name: impl Into<String>) -> Self {
171 Self {
172 name: name.into(),
173 params: Vec::new(),
174 ret_ty: None,
175 ret_signedness: Signedness::Signed,
176 ret_aggregate: None,
177 sret_claimed: false,
178 kernel: None,
179 no_inline: false,
180 inline_hint: false,
181 noreturn: false,
182 pure: false,
183 visibility: None,
184 placement: None,
185 weak: false,
186 section: None,
187 align: None,
188 naked: false,
189 variadic: false,
190 decl: None,
191 }
192 }
193
194 pub fn with_kernel(mut self, metadata: KernelMetadata) -> Self {
197 self.kernel = Some(metadata);
198 self
199 }
200
201 pub fn with_params(mut self, params: Vec<Parameter>) -> Self {
202 self.params = params;
203 self
204 }
205
206 pub fn with_return(mut self, ty: MirType) -> Self {
207 self.ret_ty = Some(ty);
208 self
209 }
210
211 pub fn with_return_signedness(mut self, signedness: Signedness) -> Self {
212 self.ret_signedness = signedness;
213 self
214 }
215
216 pub fn with_return_aggregate(mut self, aggregate: Aggregate) -> Self {
217 self.ret_aggregate = Some(aggregate);
218 self
219 }
220
221 pub fn with_decl(mut self, decl: Option<SourceLoc>) -> Self {
222 self.decl = decl;
223 self
224 }
225
226 pub fn param_count(&self) -> usize {
227 self.params.len()
228 }
229
230 pub fn is_void(&self) -> bool {
233 self.ret_ty.is_none()
234 }
235}
236
237#[derive(Debug, Clone, PartialEq)]
239pub struct Function {
240 pub sig: Signature,
242
243 pub blocks: Vec<Block>,
245
246 pub entry: String,
248
249 pub stack_reservations: HashMap<VirtualReg, usize>,
253
254 pub stack_alignments: HashMap<VirtualReg, usize>,
257
258 pub variadic_wide_arguments: HashMap<VirtualReg, Aggregate>,
264
265 pub variadic_wide_results: HashMap<VirtualReg, Aggregate>,
270
271 pub pointer_width: PointerWidth,
274
275 pub int64_regs: bool,
279
280 pub value_names: HashMap<VirtualReg, String>,
283}
284
285impl Function {
286 pub fn new(sig: Signature) -> Self {
289 Self {
290 sig,
291 blocks: Vec::new(),
292 entry: "entry".to_string(),
293 stack_reservations: HashMap::new(),
294 stack_alignments: HashMap::new(),
295 variadic_wide_arguments: HashMap::new(),
296 variadic_wide_results: HashMap::new(),
297 pointer_width: PointerWidth::Bits64,
298 int64_regs: true,
299 value_names: HashMap::new(),
300 }
301 }
302
303 pub fn with_pointer_width(mut self, pointer_width: PointerWidth) -> Self {
304 self.pointer_width = pointer_width;
305 self
306 }
307
308 pub fn with_int64_regs(mut self, int64_regs: bool) -> Self {
309 self.int64_regs = int64_regs;
310 self
311 }
312
313 pub fn variadic_wide_argument(&self, arg: &Operand) -> Option<Aggregate> {
316 match arg {
317 Operand::Register(Register::Virtual(vreg)) => {
318 self.variadic_wide_arguments.get(vreg).copied()
319 }
320 _ => None,
321 }
322 }
323
324 pub fn variadic_wide_result(&self, dst: &Register) -> Option<Aggregate> {
327 match dst {
328 Register::Virtual(vreg) => self.variadic_wide_results.get(vreg).copied(),
329 _ => None,
330 }
331 }
332
333 pub fn with_entry(mut self, entry: impl Into<String>) -> Self {
335 self.entry = entry.into();
336 self
337 }
338
339 pub fn add_block(&mut self, block: Block) {
341 self.blocks.push(block);
342 }
343
344 pub fn get_block(&self, label: &str) -> Option<&Block> {
346 self.blocks.iter().find(|b| b.label == label)
347 }
348
349 pub fn get_block_mut(&mut self, label: &str) -> Option<&mut Block> {
351 self.blocks.iter_mut().find(|b| b.label == label)
352 }
353
354 pub fn entry_block(&self) -> Option<&Block> {
356 self.get_block(&self.entry)
357 }
358
359 pub fn entry_block_mut(&mut self) -> Option<&mut Block> {
361 let entry = self.entry.clone();
362 self.get_block_mut(&entry)
363 }
364
365 pub fn block_labels(&self) -> Vec<&str> {
367 self.blocks.iter().map(|b| b.label.as_str()).collect()
368 }
369
370 pub fn instruction_count(&self) -> usize {
372 self.blocks.iter().map(Block::len).sum()
373 }
374
375 pub fn validate(&self) -> Result<(), String> {
377 verify_function(self).map_err(|errors| errors.join("\n"))
378 }
379}
380
381impl fmt::Display for Function {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 write!(f, "fn {}(", self.sig.name)?;
385 for (i, p) in self.sig.params.iter().enumerate() {
386 if i > 0 {
387 write!(f, ", ")?;
388 }
389 write!(f, "{} {}", p.reg, p.ty)?;
390 }
391 write!(f, ")")?;
392 if let Some(ret) = &self.sig.ret_ty {
393 write!(f, " -> {ret}")?;
394 }
395 writeln!(f, " {{")?;
396
397 for block in &self.blocks {
399 writeln!(f, "{block}")?;
400 }
401
402 write!(f, "}}")
403 }
404}
405
406pub struct FunctionBuilder {
408 function: Function,
409 current_block: Option<String>,
410}
411
412impl FunctionBuilder {
413 pub fn new(name: impl Into<String>) -> Self {
415 Self {
416 function: Function::new(Signature::new(name)),
417 current_block: None,
418 }
419 }
420
421 pub fn param(mut self, reg: Register, ty: MirType) -> Self {
423 self.function.sig.params.push(Parameter::new(reg, ty));
424 self
425 }
426
427 pub fn returns(mut self, ty: MirType) -> Self {
429 self.function.sig.ret_ty = Some(ty);
430 self
431 }
432
433 pub fn block(mut self, label: impl Into<String>) -> Self {
435 let label = label.into();
436 self.function.add_block(Block::new(label.clone()));
437 self.current_block = Some(label);
438 self
439 }
440
441 pub fn instr(mut self, instr: Instruction) -> Self {
443 if let Some(ref label) = self.current_block
444 && let Some(block) = self.function.get_block_mut(label)
445 {
446 block.push(instr);
447 }
448 self
449 }
450
451 pub fn build(self) -> Function {
453 self.function
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::{
461 instruction::{Instruction, IntBinOp, Operand},
462 register::VirtualReg,
463 types::ScalarType,
464 };
465
466 #[test]
467 fn test_signature_creation() {
468 let sig = Signature::new("test_func").with_return(MirType::Scalar(ScalarType::I64));
469
470 assert_eq!(sig.name, "test_func");
471 assert!(sig.ret_ty.is_some());
472 assert_eq!(sig.param_count(), 0);
473 }
474
475 #[test]
476 fn test_function_builder() {
477 let func = FunctionBuilder::new("add")
478 .param(
479 Register::Virtual(VirtualReg::gpr(0)),
480 MirType::Scalar(ScalarType::I64),
481 )
482 .param(
483 Register::Virtual(VirtualReg::gpr(1)),
484 MirType::Scalar(ScalarType::I64),
485 )
486 .returns(MirType::Scalar(ScalarType::I64))
487 .block("entry")
488 .instr(Instruction::IntBinary {
489 op: IntBinOp::Add,
490 ty: MirType::Scalar(ScalarType::I64),
491 dst: Register::Virtual(VirtualReg::gpr(2)),
492 lhs: Operand::Register(Register::Virtual(VirtualReg::gpr(0))),
493 rhs: Operand::Register(Register::Virtual(VirtualReg::gpr(1))),
494 })
495 .instr(Instruction::Ret {
496 value: Some(Operand::Register(Register::Virtual(VirtualReg::gpr(2)))),
497 })
498 .build();
499
500 assert_eq!(func.sig.name, "add");
501 assert_eq!(func.sig.param_count(), 2);
502 assert_eq!(func.blocks.len(), 1);
503 assert_eq!(func.instruction_count(), 2);
504 }
505
506 #[test]
507 fn test_function_validation() {
508 let mut func = Function::new(Signature::new("test"));
509
510 assert!(func.validate().is_ok());
512
513 let mut other = Block::new("other");
515 other.push(Instruction::Ret { value: None });
516 func.add_block(other);
517 assert!(func.validate().is_err());
518
519 let mut entry = Block::new("entry");
521 entry.push(Instruction::Ret { value: None });
522 func.add_block(entry);
523
524 assert!(func.validate().is_ok());
525 }
526}