1use crate::{
6 block::Block,
7 instruction::Instruction,
8 register::Register,
9 register::VirtualReg,
10 types::{Aggregate, MirType, Signedness},
11 verify::verify_function,
12};
13use std::collections::HashMap;
14use std::fmt;
15
16#[derive(Debug, Clone, PartialEq)]
18pub struct Parameter {
19 pub reg: Register,
21
22 pub ty: MirType,
24
25 pub signedness: Signedness,
27
28 pub aggregate: Option<Aggregate>,
31}
32
33impl Parameter {
34 pub fn new(reg: Register, ty: MirType) -> Self {
37 Self {
38 reg,
39 ty,
40 signedness: Signedness::Signed,
41 aggregate: None,
42 }
43 }
44
45 pub fn with_signedness(mut self, signedness: Signedness) -> Self {
46 self.signedness = signedness;
47 self
48 }
49
50 pub fn with_aggregate(mut self, aggregate: Aggregate) -> Self {
51 self.aggregate = Some(aggregate);
52 self
53 }
54}
55
56#[derive(Debug, Clone, Default, PartialEq, Eq)]
60pub struct KernelMetadata {
61 pub required_workgroup_size: Option<[u32; 3]>,
62 pub max_threads: Option<u32>,
63}
64
65#[derive(Debug, Clone, PartialEq)]
67pub struct Signature {
68 pub name: String,
70
71 pub params: Vec<Parameter>,
73
74 pub ret_ty: Option<MirType>,
76
77 pub ret_signedness: Signedness,
79
80 pub ret_aggregate: Option<Aggregate>,
83
84 pub kernel: Option<KernelMetadata>,
85
86 pub no_inline: bool,
88}
89
90impl Signature {
91 pub fn new(name: impl Into<String>) -> Self {
93 Self {
94 name: name.into(),
95 params: Vec::new(),
96 ret_ty: None,
97 ret_signedness: Signedness::Signed,
98 ret_aggregate: None,
99 kernel: None,
100 no_inline: false,
101 }
102 }
103
104 pub fn with_kernel(mut self, metadata: KernelMetadata) -> Self {
107 self.kernel = Some(metadata);
108 self
109 }
110
111 pub fn with_params(mut self, params: Vec<Parameter>) -> Self {
112 self.params = params;
113 self
114 }
115
116 pub fn with_return(mut self, ty: MirType) -> Self {
117 self.ret_ty = Some(ty);
118 self
119 }
120
121 pub fn with_return_signedness(mut self, signedness: Signedness) -> Self {
122 self.ret_signedness = signedness;
123 self
124 }
125
126 pub fn with_return_aggregate(mut self, aggregate: Aggregate) -> Self {
127 self.ret_aggregate = Some(aggregate);
128 self
129 }
130
131 pub fn param_count(&self) -> usize {
132 self.params.len()
133 }
134
135 pub fn is_void(&self) -> bool {
138 self.ret_ty.is_none()
139 }
140}
141
142#[derive(Debug, Clone, PartialEq)]
144pub struct Function {
145 pub sig: Signature,
147
148 pub blocks: Vec<Block>,
150
151 pub entry: String,
153
154 pub stack_reservations: HashMap<VirtualReg, usize>,
158}
159
160impl Function {
161 pub fn new(sig: Signature) -> Self {
163 Self {
164 sig,
165 blocks: Vec::new(),
166 entry: "entry".to_string(),
167 stack_reservations: HashMap::new(),
168 }
169 }
170
171 pub fn with_entry(mut self, entry: impl Into<String>) -> Self {
173 self.entry = entry.into();
174 self
175 }
176
177 pub fn add_block(&mut self, block: Block) {
179 self.blocks.push(block);
180 }
181
182 pub fn get_block(&self, label: &str) -> Option<&Block> {
184 self.blocks.iter().find(|b| b.label == label)
185 }
186
187 pub fn get_block_mut(&mut self, label: &str) -> Option<&mut Block> {
189 self.blocks.iter_mut().find(|b| b.label == label)
190 }
191
192 pub fn entry_block(&self) -> Option<&Block> {
194 self.get_block(&self.entry)
195 }
196
197 pub fn entry_block_mut(&mut self) -> Option<&mut Block> {
199 let entry = self.entry.clone();
200 self.get_block_mut(&entry)
201 }
202
203 pub fn block_labels(&self) -> Vec<&str> {
205 self.blocks.iter().map(|b| b.label.as_str()).collect()
206 }
207
208 pub fn instruction_count(&self) -> usize {
210 self.blocks.iter().map(Block::len).sum()
211 }
212
213 pub fn validate(&self) -> Result<(), String> {
215 verify_function(self).map_err(|errors| errors.join("\n"))
216 }
217}
218
219impl fmt::Display for Function {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "fn {}(", self.sig.name)?;
223 for (i, p) in self.sig.params.iter().enumerate() {
224 if i > 0 {
225 write!(f, ", ")?;
226 }
227 write!(f, "{} {}", p.reg, p.ty)?;
228 }
229 write!(f, ")")?;
230 if let Some(ret) = &self.sig.ret_ty {
231 write!(f, " -> {ret}")?;
232 }
233 writeln!(f, " {{")?;
234
235 for block in &self.blocks {
237 writeln!(f, "{block}")?;
238 }
239
240 write!(f, "}}")
241 }
242}
243
244pub struct FunctionBuilder {
246 function: Function,
247 current_block: Option<String>,
248}
249
250impl FunctionBuilder {
251 pub fn new(name: impl Into<String>) -> Self {
253 Self {
254 function: Function::new(Signature::new(name)),
255 current_block: None,
256 }
257 }
258
259 pub fn param(mut self, reg: Register, ty: MirType) -> Self {
261 self.function.sig.params.push(Parameter::new(reg, ty));
262 self
263 }
264
265 pub fn returns(mut self, ty: MirType) -> Self {
267 self.function.sig.ret_ty = Some(ty);
268 self
269 }
270
271 pub fn block(mut self, label: impl Into<String>) -> Self {
273 let label = label.into();
274 self.function.add_block(Block::new(label.clone()));
275 self.current_block = Some(label);
276 self
277 }
278
279 pub fn instr(mut self, instr: Instruction) -> Self {
281 if let Some(ref label) = self.current_block
282 && let Some(block) = self.function.get_block_mut(label)
283 {
284 block.push(instr);
285 }
286 self
287 }
288
289 pub fn build(self) -> Function {
291 self.function
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use crate::{
299 instruction::{Instruction, IntBinOp, Operand},
300 register::VirtualReg,
301 types::ScalarType,
302 };
303
304 #[test]
305 fn test_signature_creation() {
306 let sig = Signature::new("test_func").with_return(MirType::Scalar(ScalarType::I64));
307
308 assert_eq!(sig.name, "test_func");
309 assert!(sig.ret_ty.is_some());
310 assert_eq!(sig.param_count(), 0);
311 }
312
313 #[test]
314 fn test_function_builder() {
315 let func = FunctionBuilder::new("add")
316 .param(
317 Register::Virtual(VirtualReg::gpr(0)),
318 MirType::Scalar(ScalarType::I64),
319 )
320 .param(
321 Register::Virtual(VirtualReg::gpr(1)),
322 MirType::Scalar(ScalarType::I64),
323 )
324 .returns(MirType::Scalar(ScalarType::I64))
325 .block("entry")
326 .instr(Instruction::IntBinary {
327 op: IntBinOp::Add,
328 ty: MirType::Scalar(ScalarType::I64),
329 dst: Register::Virtual(VirtualReg::gpr(2)),
330 lhs: Operand::Register(Register::Virtual(VirtualReg::gpr(0))),
331 rhs: Operand::Register(Register::Virtual(VirtualReg::gpr(1))),
332 })
333 .instr(Instruction::Ret {
334 value: Some(Operand::Register(Register::Virtual(VirtualReg::gpr(2)))),
335 })
336 .build();
337
338 assert_eq!(func.sig.name, "add");
339 assert_eq!(func.sig.param_count(), 2);
340 assert_eq!(func.blocks.len(), 1);
341 assert_eq!(func.instruction_count(), 2);
342 }
343
344 #[test]
345 fn test_function_validation() {
346 let mut func = Function::new(Signature::new("test"));
347
348 assert!(func.validate().is_ok());
350
351 let mut other = Block::new("other");
353 other.push(Instruction::Ret { value: None });
354 func.add_block(other);
355 assert!(func.validate().is_err());
356
357 let mut entry = Block::new("entry");
359 entry.push(Instruction::Ret { value: None });
360 func.add_block(entry);
361
362 assert!(func.validate().is_ok());
363 }
364}