1use std::collections::{BTreeMap, HashMap};
10use std::fmt::Write;
11
12use rustc_hash::{FxHashMap, FxHashSet};
13
14use crate::{
15 block::{Block, BlockIterator, Label},
16 context::Context,
17 error::IrError,
18 irtype::Type,
19 metadata::MetadataIndex,
20 module::Module,
21 value::{Value, ValueDatum},
22 variable::{LocalVar, LocalVarContent},
23 BlockArgument, BranchToWithArgs,
24};
25use crate::{Constant, InstOp};
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
30pub struct Function(pub slotmap::DefaultKey);
31
32#[doc(hidden)]
33pub struct FunctionContent {
34 pub name: String,
35 pub abi_errors_display: String,
44 pub arguments: Vec<(String, Value)>,
45 pub return_type: Type,
46 pub blocks: Vec<Block>,
47 pub module: Module,
48 pub is_public: bool,
49 pub is_entry: bool,
50 pub is_original_entry: bool,
53 pub is_fallback: bool,
54 pub selector: Option<[u8; 4]>,
55 pub metadata: Option<MetadataIndex>,
56
57 pub local_storage: BTreeMap<String, LocalVar>, next_label_idx: u64,
60}
61
62impl Function {
63 #[allow(clippy::too_many_arguments)]
71 pub fn new(
72 context: &mut Context,
73 module: Module,
74 name: String,
75 abi_errors_display: String,
76 args: Vec<(String, Type, Option<MetadataIndex>)>,
77 return_type: Type,
78 selector: Option<[u8; 4]>,
79 is_public: bool,
80 is_entry: bool,
81 is_original_entry: bool,
82 is_fallback: bool,
83 metadata: Option<MetadataIndex>,
84 ) -> Function {
85 let content = FunctionContent {
86 name,
87 abi_errors_display,
88 arguments: Vec::new(),
91 return_type,
92 blocks: Vec::new(),
93 module,
94 is_public,
95 is_entry,
96 is_original_entry,
97 is_fallback,
98 selector,
99 metadata,
100 local_storage: BTreeMap::new(),
101 next_label_idx: 0,
102 };
103 let func = Function(context.functions.insert(content));
104
105 context.modules[module.0].functions.push(func);
106
107 let entry_block = Block::new(context, func, Some("entry".to_owned()));
108 context
109 .functions
110 .get_mut(func.0)
111 .unwrap()
112 .blocks
113 .push(entry_block);
114
115 let arguments: Vec<_> = args
117 .into_iter()
118 .enumerate()
119 .map(|(idx, (name, ty, arg_metadata))| {
120 (
121 name,
122 Value::new_argument(
123 context,
124 BlockArgument {
125 block: entry_block,
126 idx,
127 ty,
128 is_immutable: false,
129 },
130 )
131 .add_metadatum(context, arg_metadata),
132 )
133 })
134 .collect();
135 context
136 .functions
137 .get_mut(func.0)
138 .unwrap()
139 .arguments
140 .clone_from(&arguments);
141 let (_, arg_vals): (Vec<_>, Vec<_>) = arguments.iter().cloned().unzip();
142 context.blocks.get_mut(entry_block.0).unwrap().args = arg_vals;
143
144 func
145 }
146
147 pub fn is_leaf_fn(&self, context: &Context) -> bool {
149 let any_call = self
150 .instruction_iter(context)
151 .filter_map(|(_, i)| i.get_instruction(context).map(|i| i.is_call()))
152 .any(|x| x);
153 !any_call
154 }
155
156 pub fn create_block(&self, context: &mut Context, label: Option<Label>) -> Block {
158 let block = Block::new(context, *self, label);
159 let func = context.functions.get_mut(self.0).unwrap();
160 func.blocks.push(block);
161 block
162 }
163
164 pub fn create_block_before(
168 &self,
169 context: &mut Context,
170 other: &Block,
171 label: Option<Label>,
172 ) -> Result<Block, IrError> {
173 let block_idx = context.functions[self.0]
174 .blocks
175 .iter()
176 .position(|block| block == other)
177 .ok_or_else(|| {
178 let label = &context.blocks[other.0].label;
179 IrError::MissingBlock(label.clone())
180 })?;
181
182 let new_block = Block::new(context, *self, label);
183 context.functions[self.0]
184 .blocks
185 .insert(block_idx, new_block);
186 Ok(new_block)
187 }
188
189 pub fn create_block_after(
193 &self,
194 context: &mut Context,
195 other: &Block,
196 label: Option<Label>,
197 ) -> Result<Block, IrError> {
198 let new_block = Block::new(context, *self, label);
201 let func = context.functions.get_mut(self.0).unwrap();
202 func.blocks
203 .iter()
204 .position(|block| block == other)
205 .map(|idx| {
206 func.blocks.insert(idx + 1, new_block);
207 new_block
208 })
209 .ok_or_else(|| {
210 let label = &context.blocks[other.0].label;
211 IrError::MissingBlock(label.clone())
212 })
213 }
214
215 pub fn remove_block(&self, context: &mut Context, block: &Block) -> Result<(), IrError> {
220 let label = block.get_label(context);
221 let func = context.functions.get_mut(self.0).unwrap();
222 let block_idx = func
223 .blocks
224 .iter()
225 .position(|b| b == block)
226 .ok_or(IrError::RemoveMissingBlock(label))?;
227 func.blocks.remove(block_idx);
228 Ok(())
229 }
230
231 pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
233 for block in context.functions[self.0].blocks.clone() {
234 block.remove_instructions(context, &pred);
235 }
236 }
237
238 pub fn get_unique_label(&self, context: &mut Context, hint: Option<String>) -> String {
246 match hint {
247 Some(hint) => {
248 if context.functions[self.0]
249 .blocks
250 .iter()
251 .any(|block| context.blocks[block.0].label == hint)
252 {
253 let idx = self.get_next_label_idx(context);
254 self.get_unique_label(context, Some(format!("{hint}{idx}")))
255 } else {
256 hint
257 }
258 }
259 None => {
260 let idx = self.get_next_label_idx(context);
261 self.get_unique_label(context, Some(format!("block{idx}")))
262 }
263 }
264 }
265
266 fn get_next_label_idx(&self, context: &mut Context) -> u64 {
267 let func = context.functions.get_mut(self.0).unwrap();
268 let idx = func.next_label_idx;
269 func.next_label_idx += 1;
270 idx
271 }
272
273 pub fn num_blocks(&self, context: &Context) -> usize {
275 context.functions[self.0].blocks.len()
276 }
277
278 pub fn num_instructions(&self, context: &Context) -> usize {
288 self.block_iter(context)
289 .map(|block| block.num_instructions(context))
290 .sum()
291 }
292
293 pub fn num_instructions_incl_asm_instructions(&self, context: &Context) -> usize {
305 self.instruction_iter(context).fold(0, |num, (_, value)| {
306 match &value
307 .get_instruction(context)
308 .expect("We are iterating through the instructions.")
309 .op
310 {
311 InstOp::AsmBlock(asm, _) => num + asm.body.len(),
312 _ => num + 1,
313 }
314 })
315 }
316
317 pub fn get_name<'a>(&self, context: &'a Context) -> &'a str {
319 &context.functions[self.0].name
320 }
321
322 pub fn get_abi_errors_display(&self, context: &Context) -> String {
325 context.functions[self.0].abi_errors_display.clone()
326 }
327
328 pub fn get_module(&self, context: &Context) -> Module {
330 context.functions[self.0].module
331 }
332
333 pub fn get_entry_block(&self, context: &Context) -> Block {
335 context.functions[self.0].blocks[0]
336 }
337
338 pub fn get_metadata(&self, context: &Context) -> Option<MetadataIndex> {
340 context.functions[self.0].metadata
341 }
342
343 pub fn has_selector(&self, context: &Context) -> bool {
345 context.functions[self.0].selector.is_some()
346 }
347
348 pub fn get_selector(&self, context: &Context) -> Option<[u8; 4]> {
350 context.functions[self.0].selector
351 }
352
353 pub fn is_entry(&self, context: &Context) -> bool {
356 context.functions[self.0].is_entry
357 }
358
359 pub fn is_original_entry(&self, context: &Context) -> bool {
362 context.functions[self.0].is_original_entry
363 }
364
365 pub fn is_fallback(&self, context: &Context) -> bool {
367 context.functions[self.0].is_fallback
368 }
369
370 pub fn get_return_type(&self, context: &Context) -> Type {
372 context.functions[self.0].return_type
373 }
374
375 pub fn set_return_type(&self, context: &mut Context, new_ret_type: Type) {
377 context.functions.get_mut(self.0).unwrap().return_type = new_ret_type
378 }
379
380 pub fn num_args(&self, context: &Context) -> usize {
382 context.functions[self.0].arguments.len()
383 }
384
385 pub fn get_arg(&self, context: &Context, name: &str) -> Option<Value> {
387 context.functions[self.0]
388 .arguments
389 .iter()
390 .find_map(|(arg_name, val)| (arg_name == name).then_some(val))
391 .copied()
392 }
393
394 pub fn add_arg<S: Into<String>>(&self, context: &mut Context, name: S, arg: Value) {
399 match context.values[arg.0].value {
400 ValueDatum::Argument(BlockArgument { idx, .. })
401 if idx == context.functions[self.0].arguments.len() =>
402 {
403 context.functions[self.0].arguments.push((name.into(), arg));
404 }
405 _ => panic!("Inconsistent function argument being added"),
406 }
407 }
408
409 pub fn lookup_arg_name<'a>(&self, context: &'a Context, value: &Value) -> Option<&'a String> {
411 context.functions[self.0]
412 .arguments
413 .iter()
414 .find_map(|(name, arg_val)| (arg_val == value).then_some(name))
415 }
416
417 pub fn args_iter<'a>(&self, context: &'a Context) -> impl Iterator<Item = &'a (String, Value)> {
419 context.functions[self.0].arguments.iter()
420 }
421
422 pub fn is_arg_immutable(&self, context: &Context, i: usize) -> bool {
424 if let Some((_, val)) = context.functions[self.0].arguments.get(i) {
425 if let ValueDatum::Argument(arg) = &context.values[val.0].value {
426 return arg.is_immutable;
427 }
428 }
429 false
430 }
431
432 pub fn get_local_var(&self, context: &Context, name: &str) -> Option<LocalVar> {
434 context.functions[self.0].local_storage.get(name).copied()
435 }
436
437 pub fn lookup_local_name<'a>(
439 &self,
440 context: &'a Context,
441 var: &LocalVar,
442 ) -> Option<&'a String> {
443 context.functions[self.0]
444 .local_storage
445 .iter()
446 .find_map(|(name, local_var)| if local_var == var { Some(name) } else { None })
447 }
448
449 pub fn new_local_var(
453 &self,
454 context: &mut Context,
455 name: String,
456 local_type: Type,
457 initializer: Option<Constant>,
458 mutable: bool,
459 ) -> Result<LocalVar, IrError> {
460 let var = LocalVar::new(context, local_type, initializer, mutable);
461 let func = context.functions.get_mut(self.0).unwrap();
462 func.local_storage
463 .insert(name.clone(), var)
464 .map(|_| Err(IrError::FunctionLocalClobbered(func.name.clone(), name)))
465 .unwrap_or(Ok(var))
466 }
467
468 pub fn new_unique_local_var(
472 &self,
473 context: &mut Context,
474 name: String,
475 local_type: Type,
476 initializer: Option<Constant>,
477 mutable: bool,
478 ) -> LocalVar {
479 let func = &context.functions[self.0];
480 let new_name = if func.local_storage.contains_key(&name) {
481 (0..)
484 .find_map(|n| {
485 let candidate = format!("{name}{n}");
486 if func.local_storage.contains_key(&candidate) {
487 None
488 } else {
489 Some(candidate)
490 }
491 })
492 .unwrap()
493 } else {
494 name
495 };
496 self.new_local_var(context, new_name, local_type, initializer, mutable)
497 .unwrap()
498 }
499
500 pub fn locals_iter<'a>(
502 &self,
503 context: &'a Context,
504 ) -> impl Iterator<Item = (&'a String, &'a LocalVar)> {
505 context.functions[self.0].local_storage.iter()
506 }
507
508 pub fn remove_locals(&self, context: &mut Context, removals: &Vec<String>) {
510 for remove in removals {
511 if let Some(local) = context.functions[self.0].local_storage.remove(remove) {
512 context.local_vars.remove(local.0);
513 }
514 }
515 }
516
517 pub fn merge_locals_from(
524 &self,
525 context: &mut Context,
526 other: Function,
527 ) -> HashMap<LocalVar, LocalVar> {
528 let mut var_map = HashMap::new();
529 let old_vars: Vec<(String, LocalVar, LocalVarContent)> = context.functions[other.0]
530 .local_storage
531 .iter()
532 .map(|(name, var)| (name.clone(), *var, context.local_vars[var.0].clone()))
533 .collect();
534 for (name, old_var, old_var_content) in old_vars {
535 let old_ty = old_var_content
536 .ptr_ty
537 .get_pointee_type(context)
538 .expect("LocalVar types are always pointers.");
539 let new_var = self.new_unique_local_var(
540 context,
541 name.clone(),
542 old_ty,
543 old_var_content.initializer,
544 old_var_content.mutable,
545 );
546 var_map.insert(old_var, new_var);
547 }
548 var_map
549 }
550
551 pub fn block_iter(&self, context: &Context) -> BlockIterator {
553 BlockIterator::new(context, self)
554 }
555
556 pub fn instruction_iter<'a>(
561 &self,
562 context: &'a Context,
563 ) -> impl Iterator<Item = (Block, Value)> + 'a {
564 context.functions[self.0]
565 .blocks
566 .iter()
567 .flat_map(move |block| {
568 block
569 .instruction_iter(context)
570 .map(move |ins_val| (*block, ins_val))
571 })
572 }
573
574 pub fn instruction_iter_rev<'a>(
582 &self,
583 context: &'a Context,
584 ) -> impl Iterator<Item = (Block, Value)> + 'a {
585 context.functions[self.0]
586 .blocks
587 .iter()
588 .rev()
589 .flat_map(move |block| {
590 block
591 .instruction_iter(context)
592 .rev()
593 .map(move |ins_val| (*block, ins_val))
594 })
595 }
596
597 pub fn replace_values(
605 &self,
606 context: &mut Context,
607 replace_map: &FxHashMap<Value, Value>,
608 starting_block: Option<Block>,
609 ) {
610 let mut block_iter = self.block_iter(context).peekable();
611
612 if let Some(ref starting_block) = starting_block {
613 while block_iter
615 .next_if(|block| block != starting_block)
616 .is_some()
617 {}
618 }
619
620 for block in block_iter {
621 block.replace_values(context, replace_map);
622 }
623 }
624
625 pub fn replace_value(
626 &self,
627 context: &mut Context,
628 old_val: Value,
629 new_val: Value,
630 starting_block: Option<Block>,
631 ) {
632 let mut map = FxHashMap::<Value, Value>::default();
633 map.insert(old_val, new_val);
634 self.replace_values(context, &map, starting_block);
635 }
636
637 pub fn dot_cfg(&self, context: &Context) -> String {
639 let mut worklist = Vec::<Block>::new();
640 let mut visited = FxHashSet::<Block>::default();
641 let entry = self.get_entry_block(context);
642 let mut res = format!("digraph {} {{\n", self.get_name(context));
643
644 worklist.push(entry);
645 while let Some(n) = worklist.pop() {
646 visited.insert(n);
647 for BranchToWithArgs { block: n_succ, .. } in n.successors(context) {
648 let _ = writeln!(
649 res,
650 "\t{} -> {}\n",
651 n.get_label(context),
652 n_succ.get_label(context)
653 );
654 if !visited.contains(&n_succ) {
655 worklist.push(n_succ);
656 }
657 }
658 }
659
660 res += "}\n";
661 res
662 }
663}
664
665pub struct FunctionIterator {
667 functions: Vec<slotmap::DefaultKey>,
668 next: usize,
669}
670
671impl FunctionIterator {
672 pub fn new(context: &Context, module: &Module) -> FunctionIterator {
674 FunctionIterator {
677 functions: context.modules[module.0]
678 .functions
679 .iter()
680 .map(|func| func.0)
681 .collect(),
682 next: 0,
683 }
684 }
685}
686
687impl Iterator for FunctionIterator {
688 type Item = Function;
689
690 fn next(&mut self) -> Option<Function> {
691 if self.next < self.functions.len() {
692 let idx = self.next;
693 self.next += 1;
694 Some(Function(self.functions[idx]))
695 } else {
696 None
697 }
698 }
699}