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, Debug)]
28pub enum IrMutability {
29 Mutable,
30 Immutable,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
36pub struct Function(pub slotmap::DefaultKey);
37
38#[derive(Clone)]
39pub struct FunctionArgContent {
40 pub mutability: IrMutability,
41 pub name: String,
42 pub value: Value,
43}
44
45#[doc(hidden)]
46pub struct FunctionContent {
47 pub name: String,
48 pub abi_errors_display: String,
57 pub arguments: Vec<FunctionArgContent>,
58 pub return_type: Type,
59 pub blocks: Vec<Block>,
60 pub module: Module,
61 pub is_public: bool,
62 pub is_entry: bool,
63 pub is_original_entry: bool,
66 pub is_fallback: bool,
67 pub selector: Option<[u8; 4]>,
68 pub metadata: Option<MetadataIndex>,
69
70 pub local_storage: BTreeMap<String, LocalVar>, next_label_idx: u64,
73}
74
75impl Function {
76 #[allow(clippy::too_many_arguments)]
84 pub fn new(
85 context: &mut Context,
86 module: Module,
87 name: String,
88 abi_errors_display: String,
89 args: Vec<(IrMutability, String, Type, Option<MetadataIndex>)>,
90 return_type: Type,
91 selector: Option<[u8; 4]>,
92 is_public: bool,
93 is_entry: bool,
94 is_original_entry: bool,
95 is_fallback: bool,
96 metadata: Option<MetadataIndex>,
97 ) -> Function {
98 let content = FunctionContent {
99 name,
100 abi_errors_display,
101 arguments: Vec::new(),
104 return_type,
105 blocks: Vec::new(),
106 module,
107 is_public,
108 is_entry,
109 is_original_entry,
110 is_fallback,
111 selector,
112 metadata,
113 local_storage: BTreeMap::new(),
114 next_label_idx: 0,
115 };
116 let func = Function(context.functions.insert(content));
117
118 context.modules[module.0].functions.push(func);
119
120 let entry_block = Block::new(context, func, Some("entry".to_owned()));
121 context
122 .functions
123 .get_mut(func.0)
124 .unwrap()
125 .blocks
126 .push(entry_block);
127
128 let arguments: Vec<_> = args
130 .into_iter()
131 .enumerate()
132 .map(
133 |(idx, (mutability, name, ty, arg_metadata))| FunctionArgContent {
134 mutability,
135 name,
136 value: Value::new_argument(
137 context,
138 BlockArgument {
139 block: entry_block,
140 idx,
141 ty,
142 is_immutable: false,
143 },
144 )
145 .add_metadatum(context, arg_metadata),
146 },
147 )
148 .collect();
149
150 context
151 .functions
152 .get_mut(func.0)
153 .unwrap()
154 .arguments
155 .clone_from(&arguments);
156
157 let arg_vals = arguments.iter().map(|x| x.value).collect();
158 context.blocks.get_mut(entry_block.0).unwrap().args = arg_vals;
159
160 func
161 }
162
163 pub fn is_leaf_fn(&self, context: &Context) -> bool {
165 let any_call = self
166 .instruction_iter(context)
167 .filter_map(|(_, i)| i.get_instruction(context).map(|i| i.is_call()))
168 .any(|x| x);
169 !any_call
170 }
171
172 pub fn create_block(&self, context: &mut Context, label: Option<Label>) -> Block {
174 let block = Block::new(context, *self, label);
175 let func = context.functions.get_mut(self.0).unwrap();
176 func.blocks.push(block);
177 block
178 }
179
180 pub fn create_block_before(
184 &self,
185 context: &mut Context,
186 other: &Block,
187 label: Option<Label>,
188 ) -> Result<Block, IrError> {
189 let block_idx = context.functions[self.0]
190 .blocks
191 .iter()
192 .position(|block| block == other)
193 .ok_or_else(|| {
194 let label = &context.blocks[other.0].label;
195 IrError::MissingBlock(label.clone())
196 })?;
197
198 let new_block = Block::new(context, *self, label);
199 context.functions[self.0]
200 .blocks
201 .insert(block_idx, new_block);
202 Ok(new_block)
203 }
204
205 pub fn create_block_after(
209 &self,
210 context: &mut Context,
211 other: &Block,
212 label: Option<Label>,
213 ) -> Result<Block, IrError> {
214 let new_block = Block::new(context, *self, label);
217 let func = context.functions.get_mut(self.0).unwrap();
218 func.blocks
219 .iter()
220 .position(|block| block == other)
221 .map(|idx| {
222 func.blocks.insert(idx + 1, new_block);
223 new_block
224 })
225 .ok_or_else(|| {
226 let label = &context.blocks[other.0].label;
227 IrError::MissingBlock(label.clone())
228 })
229 }
230
231 pub fn remove_block(&self, context: &mut Context, block: &Block) -> Result<(), IrError> {
236 let label = block.get_label(context).to_string();
237 let func = context.functions.get_mut(self.0).unwrap();
238 let block_idx = func
239 .blocks
240 .iter()
241 .position(|b| b == block)
242 .ok_or(IrError::RemoveMissingBlock(label))?;
243 func.blocks.remove(block_idx);
244 Ok(())
245 }
246
247 pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
249 for block in context.functions[self.0].blocks.clone() {
250 block.remove_instructions(context, &pred);
251 }
252 }
253
254 pub fn get_unique_label(&self, context: &mut Context, hint: Option<String>) -> String {
262 match hint {
263 Some(hint) => {
264 if context.functions[self.0]
265 .blocks
266 .iter()
267 .any(|block| context.blocks[block.0].label == hint)
268 {
269 let idx = self.get_next_label_idx(context);
270 self.get_unique_label(context, Some(format!("{hint}{idx}")))
271 } else {
272 hint
273 }
274 }
275 None => {
276 let idx = self.get_next_label_idx(context);
277 self.get_unique_label(context, Some(format!("block{idx}")))
278 }
279 }
280 }
281
282 fn get_next_label_idx(&self, context: &mut Context) -> u64 {
283 let func = context.functions.get_mut(self.0).unwrap();
284 let idx = func.next_label_idx;
285 func.next_label_idx += 1;
286 idx
287 }
288
289 pub fn num_blocks(&self, context: &Context) -> usize {
291 context.functions[self.0].blocks.len()
292 }
293
294 pub fn num_instructions(&self, context: &Context) -> usize {
304 self.block_iter(context)
305 .map(|block| block.num_instructions(context))
306 .sum()
307 }
308
309 pub fn num_instructions_incl_asm_instructions(&self, context: &Context) -> usize {
321 self.instruction_iter(context).fold(0, |num, (_, value)| {
322 match &value
323 .get_instruction(context)
324 .expect("We are iterating through the instructions.")
325 .op
326 {
327 InstOp::AsmBlock(asm, _) => num + asm.body.len(),
328 _ => num + 1,
329 }
330 })
331 }
332
333 pub fn get_name<'a>(&self, context: &'a Context) -> &'a str {
335 &context.functions[self.0].name
336 }
337
338 pub fn get_abi_errors_display(&self, context: &Context) -> String {
341 context.functions[self.0].abi_errors_display.clone()
342 }
343
344 pub fn get_module(&self, context: &Context) -> Module {
346 context.functions[self.0].module
347 }
348
349 pub fn get_entry_block(&self, context: &Context) -> Block {
351 context.functions[self.0].blocks[0]
352 }
353
354 pub fn get_metadata(&self, context: &Context) -> Option<MetadataIndex> {
356 context.functions[self.0].metadata
357 }
358
359 pub fn has_selector(&self, context: &Context) -> bool {
361 context.functions[self.0].selector.is_some()
362 }
363
364 pub fn get_selector(&self, context: &Context) -> Option<[u8; 4]> {
366 context.functions[self.0].selector
367 }
368
369 pub fn is_entry(&self, context: &Context) -> bool {
372 context.functions[self.0].is_entry
373 }
374
375 pub fn is_original_entry(&self, context: &Context) -> bool {
378 context.functions[self.0].is_original_entry
379 }
380
381 pub fn is_fallback(&self, context: &Context) -> bool {
383 context.functions[self.0].is_fallback
384 }
385
386 pub fn get_return_type(&self, context: &Context) -> Type {
388 context.functions[self.0].return_type
389 }
390
391 pub fn set_return_type(&self, context: &mut Context, new_ret_type: Type) {
393 context.functions.get_mut(self.0).unwrap().return_type = new_ret_type
394 }
395
396 pub fn num_args(&self, context: &Context) -> usize {
398 context.functions[self.0].arguments.len()
399 }
400
401 pub fn get_arg(&self, context: &Context, name: &str) -> Option<Value> {
403 context.functions[self.0]
404 .arguments
405 .iter()
406 .find_map(|arg| (arg.name == name).then_some(arg.value))
407 }
408
409 pub fn add_arg<S: Into<String>>(
414 &self,
415 context: &mut Context,
416 mutability: IrMutability,
417 name: S,
418 arg: Value,
419 ) {
420 match context.values[arg.0].value {
421 ValueDatum::Argument(BlockArgument { idx, .. })
422 if idx == context.functions[self.0].arguments.len() =>
423 {
424 context.functions[self.0]
425 .arguments
426 .push(FunctionArgContent {
427 mutability,
428 name: name.into(),
429 value: arg,
430 });
431 }
432 _ => panic!("Inconsistent function argument being added"),
433 }
434 }
435
436 pub fn lookup_arg_name<'a>(&self, context: &'a Context, value: &Value) -> Option<&'a String> {
438 context.functions[self.0]
439 .arguments
440 .iter()
441 .find_map(|arg| (arg.value == *value).then_some(&arg.name))
442 }
443
444 pub fn args_iter<'a>(
446 &self,
447 context: &'a Context,
448 ) -> impl Iterator<Item = &'a FunctionArgContent> {
449 context.functions[self.0].arguments.iter()
450 }
451
452 pub fn is_arg_immutable(&self, context: &Context, i: usize) -> bool {
454 if let Some(arg) = context.functions[self.0].arguments.get(i) {
455 if let ValueDatum::Argument(arg) = &context.values[arg.value.0].value {
456 return arg.is_immutable;
457 }
458 }
459 false
460 }
461
462 pub fn get_local_var(&self, context: &Context, name: &str) -> Option<LocalVar> {
464 context.functions[self.0].local_storage.get(name).copied()
465 }
466
467 pub fn lookup_local_name<'a>(
469 &self,
470 context: &'a Context,
471 var: &LocalVar,
472 ) -> Option<&'a String> {
473 context.functions[self.0]
474 .local_storage
475 .iter()
476 .find_map(|(name, local_var)| if local_var == var { Some(name) } else { None })
477 }
478
479 pub fn new_local_var(
483 &self,
484 context: &mut Context,
485 name: String,
486 local_type: Type,
487 initializer: Option<Constant>,
488 mutable: bool,
489 ) -> Result<LocalVar, IrError> {
490 let var = LocalVar::new(context, local_type, initializer, mutable);
491 let func = context.functions.get_mut(self.0).unwrap();
492 func.local_storage
493 .insert(name.clone(), var)
494 .map(|_| Err(IrError::FunctionLocalClobbered(func.name.clone(), name)))
495 .unwrap_or(Ok(var))
496 }
497
498 pub fn new_unique_local_var(
502 &self,
503 context: &mut Context,
504 name: String,
505 local_type: Type,
506 initializer: Option<Constant>,
507 mutable: bool,
508 ) -> LocalVar {
509 let func = &context.functions[self.0];
510 let new_name = if func.local_storage.contains_key(&name) {
511 (0..)
514 .find_map(|n| {
515 let candidate = format!("{name}{n}");
516 if func.local_storage.contains_key(&candidate) {
517 None
518 } else {
519 Some(candidate)
520 }
521 })
522 .unwrap()
523 } else {
524 name
525 };
526 self.new_local_var(context, new_name, local_type, initializer, mutable)
527 .unwrap()
528 }
529
530 pub fn locals_iter<'a>(
532 &self,
533 context: &'a Context,
534 ) -> impl Iterator<Item = (&'a String, &'a LocalVar)> {
535 context.functions[self.0].local_storage.iter()
536 }
537
538 pub fn remove_locals(&self, context: &mut Context, removals: &Vec<String>) -> bool {
540 let mut modified = false;
541
542 for remove in removals {
543 if let Some(local) = context.functions[self.0].local_storage.remove(remove) {
544 modified = true;
545 context.local_vars.remove(local.0);
546 }
547 }
548
549 modified
550 }
551
552 pub fn merge_locals_from(
559 &self,
560 context: &mut Context,
561 other: Function,
562 ) -> HashMap<LocalVar, LocalVar> {
563 let mut var_map = HashMap::new();
564 let old_vars: Vec<(String, LocalVar, LocalVarContent)> = context.functions[other.0]
565 .local_storage
566 .iter()
567 .map(|(name, var)| (name.clone(), *var, context.local_vars[var.0].clone()))
568 .collect();
569 for (name, old_var, old_var_content) in old_vars {
570 let old_ty = old_var_content
571 .ptr_ty
572 .get_pointee_type(context)
573 .expect("LocalVar types are always pointers.");
574 let new_var = self.new_unique_local_var(
575 context,
576 name.clone(),
577 old_ty,
578 old_var_content.initializer,
579 old_var_content.mutable,
580 );
581 var_map.insert(old_var, new_var);
582 }
583 var_map
584 }
585
586 pub fn block_iter(&self, context: &Context) -> BlockIterator {
588 BlockIterator::new(context, self)
589 }
590
591 pub fn instruction_iter<'a>(
596 &self,
597 context: &'a Context,
598 ) -> impl Iterator<Item = (Block, Value)> + 'a {
599 context.functions[self.0]
600 .blocks
601 .iter()
602 .flat_map(move |block| {
603 block
604 .instruction_iter(context)
605 .map(move |ins_val| (*block, ins_val))
606 })
607 }
608
609 pub fn instruction_iter_rev<'a>(
617 &self,
618 context: &'a Context,
619 ) -> impl Iterator<Item = (Block, Value)> + 'a {
620 context.functions[self.0]
621 .blocks
622 .iter()
623 .rev()
624 .flat_map(move |block| {
625 block
626 .instruction_iter(context)
627 .rev()
628 .map(move |ins_val| (*block, ins_val))
629 })
630 }
631
632 pub fn replace_values(
640 &self,
641 context: &mut Context,
642 replace_map: &FxHashMap<Value, Value>,
643 starting_block: Option<Block>,
644 ) -> bool {
645 let mut modified = false;
646
647 let mut block_iter = self.block_iter(context).peekable();
648
649 if let Some(ref starting_block) = starting_block {
650 while block_iter
652 .next_if(|block| block != starting_block)
653 .is_some()
654 {}
655 }
656
657 for block in block_iter {
658 modified |= block.replace_values(context, replace_map);
659 }
660
661 modified
662 }
663
664 pub fn replace_value(
665 &self,
666 context: &mut Context,
667 old_val: Value,
668 new_val: Value,
669 starting_block: Option<Block>,
670 ) {
671 let mut map = FxHashMap::<Value, Value>::default();
672 map.insert(old_val, new_val);
673 self.replace_values(context, &map, starting_block);
674 }
675
676 pub fn dot_cfg(&self, context: &Context) -> String {
678 let mut worklist = Vec::<Block>::new();
679 let mut visited = FxHashSet::<Block>::default();
680 let entry = self.get_entry_block(context);
681 let mut res = format!("digraph {} {{\n", self.get_name(context));
682
683 worklist.push(entry);
684 while let Some(n) = worklist.pop() {
685 visited.insert(n);
686 for BranchToWithArgs { block: n_succ, .. } in n.successors(context) {
687 let _ = writeln!(
688 res,
689 "\t{} -> {}\n",
690 n.get_label(context),
691 n_succ.get_label(context)
692 );
693 if !visited.contains(&n_succ) {
694 worklist.push(n_succ);
695 }
696 }
697 }
698
699 res += "}\n";
700 res
701 }
702}
703
704pub struct FunctionIterator {
706 functions: Vec<slotmap::DefaultKey>,
707 next: usize,
708}
709
710impl FunctionIterator {
711 pub fn new(context: &Context, module: &Module) -> FunctionIterator {
713 FunctionIterator {
716 functions: context.modules[module.0]
717 .functions
718 .iter()
719 .map(|func| func.0)
720 .collect(),
721 next: 0,
722 }
723 }
724}
725
726impl Iterator for FunctionIterator {
727 type Item = Function;
728
729 fn next(&mut self) -> Option<Function> {
730 if self.next < self.functions.len() {
731 let idx = self.next;
732 self.next += 1;
733 Some(Function(self.functions[idx]))
734 } else {
735 None
736 }
737 }
738}