1use lamina_platform::PointerWidth;
6
7use crate::{
8 block::Block,
9 instruction::Instruction,
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
35impl Parameter {
36 pub fn new(reg: Register, ty: MirType) -> Self {
39 Self {
40 reg,
41 ty,
42 signedness: Signedness::Signed,
43 aggregate: None,
44 }
45 }
46
47 pub fn with_signedness(mut self, signedness: Signedness) -> Self {
48 self.signedness = signedness;
49 self
50 }
51
52 pub fn with_aggregate(mut self, aggregate: Aggregate) -> Self {
53 self.aggregate = Some(aggregate);
54 self
55 }
56}
57
58#[derive(Debug, Clone, Default, PartialEq, Eq)]
62pub struct KernelMetadata {
63 pub required_workgroup_size: Option<[u32; 3]>,
64 pub max_threads: Option<u32>,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum SymbolVisibility {
70 Internal,
71 Private,
72 Hidden,
73 Protected,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum CodePlacement {
79 Cold,
80 Hot,
81}
82
83#[derive(Debug, Clone, PartialEq)]
85pub struct Signature {
86 pub name: String,
88
89 pub params: Vec<Parameter>,
91
92 pub ret_ty: Option<MirType>,
94
95 pub ret_signedness: Signedness,
97
98 pub ret_aggregate: Option<Aggregate>,
101
102 pub kernel: Option<KernelMetadata>,
103
104 pub no_inline: bool,
106
107 pub inline_hint: bool,
109
110 pub noreturn: bool,
112
113 pub pure: bool,
115
116 pub visibility: Option<SymbolVisibility>,
118
119 pub placement: Option<CodePlacement>,
121
122 pub weak: bool,
124
125 pub section: Option<String>,
127
128 pub align: Option<u32>,
130
131 pub naked: bool,
133
134 pub variadic: bool,
137}
138
139impl Signature {
140 pub fn new(name: impl Into<String>) -> Self {
142 Self {
143 name: name.into(),
144 params: Vec::new(),
145 ret_ty: None,
146 ret_signedness: Signedness::Signed,
147 ret_aggregate: None,
148 kernel: None,
149 no_inline: false,
150 inline_hint: false,
151 noreturn: false,
152 pure: false,
153 visibility: None,
154 placement: None,
155 weak: false,
156 section: None,
157 align: None,
158 naked: false,
159 variadic: false,
160 }
161 }
162
163 pub fn with_kernel(mut self, metadata: KernelMetadata) -> Self {
166 self.kernel = Some(metadata);
167 self
168 }
169
170 pub fn with_params(mut self, params: Vec<Parameter>) -> Self {
171 self.params = params;
172 self
173 }
174
175 pub fn with_return(mut self, ty: MirType) -> Self {
176 self.ret_ty = Some(ty);
177 self
178 }
179
180 pub fn with_return_signedness(mut self, signedness: Signedness) -> Self {
181 self.ret_signedness = signedness;
182 self
183 }
184
185 pub fn with_return_aggregate(mut self, aggregate: Aggregate) -> Self {
186 self.ret_aggregate = Some(aggregate);
187 self
188 }
189
190 pub fn param_count(&self) -> usize {
191 self.params.len()
192 }
193
194 pub fn is_void(&self) -> bool {
197 self.ret_ty.is_none()
198 }
199}
200
201#[derive(Debug, Clone, PartialEq)]
203pub struct Function {
204 pub sig: Signature,
206
207 pub blocks: Vec<Block>,
209
210 pub entry: String,
212
213 pub stack_reservations: HashMap<VirtualReg, usize>,
217
218 pub stack_alignments: HashMap<VirtualReg, usize>,
221
222 pub pointer_width: PointerWidth,
225
226 pub int64_regs: bool,
230}
231
232impl Function {
233 pub fn new(sig: Signature) -> Self {
236 Self {
237 sig,
238 blocks: Vec::new(),
239 entry: "entry".to_string(),
240 stack_reservations: HashMap::new(),
241 stack_alignments: HashMap::new(),
242 pointer_width: PointerWidth::Bits64,
243 int64_regs: true,
244 }
245 }
246
247 pub fn with_pointer_width(mut self, pointer_width: PointerWidth) -> Self {
248 self.pointer_width = pointer_width;
249 self
250 }
251
252 pub fn with_int64_regs(mut self, int64_regs: bool) -> Self {
253 self.int64_regs = int64_regs;
254 self
255 }
256
257 pub fn with_entry(mut self, entry: impl Into<String>) -> Self {
259 self.entry = entry.into();
260 self
261 }
262
263 pub fn add_block(&mut self, block: Block) {
265 self.blocks.push(block);
266 }
267
268 pub fn get_block(&self, label: &str) -> Option<&Block> {
270 self.blocks.iter().find(|b| b.label == label)
271 }
272
273 pub fn get_block_mut(&mut self, label: &str) -> Option<&mut Block> {
275 self.blocks.iter_mut().find(|b| b.label == label)
276 }
277
278 pub fn entry_block(&self) -> Option<&Block> {
280 self.get_block(&self.entry)
281 }
282
283 pub fn entry_block_mut(&mut self) -> Option<&mut Block> {
285 let entry = self.entry.clone();
286 self.get_block_mut(&entry)
287 }
288
289 pub fn block_labels(&self) -> Vec<&str> {
291 self.blocks.iter().map(|b| b.label.as_str()).collect()
292 }
293
294 pub fn instruction_count(&self) -> usize {
296 self.blocks.iter().map(Block::len).sum()
297 }
298
299 pub fn validate(&self) -> Result<(), String> {
301 verify_function(self).map_err(|errors| errors.join("\n"))
302 }
303}
304
305impl fmt::Display for Function {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 write!(f, "fn {}(", self.sig.name)?;
309 for (i, p) in self.sig.params.iter().enumerate() {
310 if i > 0 {
311 write!(f, ", ")?;
312 }
313 write!(f, "{} {}", p.reg, p.ty)?;
314 }
315 write!(f, ")")?;
316 if let Some(ret) = &self.sig.ret_ty {
317 write!(f, " -> {ret}")?;
318 }
319 writeln!(f, " {{")?;
320
321 for block in &self.blocks {
323 writeln!(f, "{block}")?;
324 }
325
326 write!(f, "}}")
327 }
328}
329
330pub struct FunctionBuilder {
332 function: Function,
333 current_block: Option<String>,
334}
335
336impl FunctionBuilder {
337 pub fn new(name: impl Into<String>) -> Self {
339 Self {
340 function: Function::new(Signature::new(name)),
341 current_block: None,
342 }
343 }
344
345 pub fn param(mut self, reg: Register, ty: MirType) -> Self {
347 self.function.sig.params.push(Parameter::new(reg, ty));
348 self
349 }
350
351 pub fn returns(mut self, ty: MirType) -> Self {
353 self.function.sig.ret_ty = Some(ty);
354 self
355 }
356
357 pub fn block(mut self, label: impl Into<String>) -> Self {
359 let label = label.into();
360 self.function.add_block(Block::new(label.clone()));
361 self.current_block = Some(label);
362 self
363 }
364
365 pub fn instr(mut self, instr: Instruction) -> Self {
367 if let Some(ref label) = self.current_block
368 && let Some(block) = self.function.get_block_mut(label)
369 {
370 block.push(instr);
371 }
372 self
373 }
374
375 pub fn build(self) -> Function {
377 self.function
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384 use crate::{
385 instruction::{Instruction, IntBinOp, Operand},
386 register::VirtualReg,
387 types::ScalarType,
388 };
389
390 #[test]
391 fn test_signature_creation() {
392 let sig = Signature::new("test_func").with_return(MirType::Scalar(ScalarType::I64));
393
394 assert_eq!(sig.name, "test_func");
395 assert!(sig.ret_ty.is_some());
396 assert_eq!(sig.param_count(), 0);
397 }
398
399 #[test]
400 fn test_function_builder() {
401 let func = FunctionBuilder::new("add")
402 .param(
403 Register::Virtual(VirtualReg::gpr(0)),
404 MirType::Scalar(ScalarType::I64),
405 )
406 .param(
407 Register::Virtual(VirtualReg::gpr(1)),
408 MirType::Scalar(ScalarType::I64),
409 )
410 .returns(MirType::Scalar(ScalarType::I64))
411 .block("entry")
412 .instr(Instruction::IntBinary {
413 op: IntBinOp::Add,
414 ty: MirType::Scalar(ScalarType::I64),
415 dst: Register::Virtual(VirtualReg::gpr(2)),
416 lhs: Operand::Register(Register::Virtual(VirtualReg::gpr(0))),
417 rhs: Operand::Register(Register::Virtual(VirtualReg::gpr(1))),
418 })
419 .instr(Instruction::Ret {
420 value: Some(Operand::Register(Register::Virtual(VirtualReg::gpr(2)))),
421 })
422 .build();
423
424 assert_eq!(func.sig.name, "add");
425 assert_eq!(func.sig.param_count(), 2);
426 assert_eq!(func.blocks.len(), 1);
427 assert_eq!(func.instruction_count(), 2);
428 }
429
430 #[test]
431 fn test_function_validation() {
432 let mut func = Function::new(Signature::new("test"));
433
434 assert!(func.validate().is_ok());
436
437 let mut other = Block::new("other");
439 other.push(Instruction::Ret { value: None });
440 func.add_block(other);
441 assert!(func.validate().is_err());
442
443 let mut entry = Block::new("entry");
445 entry.push(Instruction::Ret { value: None });
446 func.add_block(entry);
447
448 assert!(func.validate().is_ok());
449 }
450}