1use rustc_hash::{FxHashMap, FxHashSet};
14
15use crate::{
16 context::Context,
17 error::IrError,
18 function::Function,
19 instruction::{FuelVmInstruction, InstOp},
20 value::{Value, ValueDatum},
21 BranchToWithArgs, DebugWithContext, Instruction, InstructionInserter, InstructionIterator,
22 Module, Type,
23};
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)]
28pub struct Block(pub slotmap::DefaultKey);
29
30impl Block {
31 pub fn get_module<'a>(&self, context: &'a Context) -> &'a Module {
32 let f = context.blocks[self.0].function;
33 &context.functions[f.0].module
34 }
35}
36
37#[doc(hidden)]
38pub struct BlockContent {
39 pub label: Label,
41 pub function: Function,
43 pub(crate) instructions: Vec<Value>,
45 pub args: Vec<Value>,
47 pub preds: FxHashSet<Block>,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DebugWithContext)]
52pub struct BlockArgument {
53 pub block: Block,
55 pub idx: usize,
57 pub ty: Type,
58}
59
60impl BlockArgument {
61 pub fn get_val_coming_from(&self, context: &Context, from_block: &Block) -> Option<Value> {
63 for BranchToWithArgs {
64 block: succ_block,
65 args,
66 } in from_block.successors(context)
67 {
68 if succ_block == self.block {
69 return Some(args[self.idx]);
70 }
71 }
72 None
73 }
74}
75
76pub type Label = String;
78
79impl Block {
80 pub fn new(context: &mut Context, function: Function, label: Option<String>) -> Block {
85 let label = function.get_unique_label(context, label);
86 let content = BlockContent {
87 label,
88 function,
89 instructions: vec![],
90 args: vec![],
91 preds: FxHashSet::default(),
92 };
93 Block(context.blocks.insert(content))
94 }
95
96 pub fn get_function(&self, context: &Context) -> Function {
98 context.blocks[self.0].function
99 }
100
101 pub fn append<'a, 'eng>(
103 &self,
104 context: &'a mut Context<'eng>,
105 ) -> InstructionInserter<'a, 'eng> {
106 InstructionInserter::new(context, *self, crate::InsertionPosition::End)
107 }
108
109 pub fn get_label(&self, context: &Context) -> String {
112 context.blocks[self.0].label.clone()
113 }
114
115 pub fn set_label(&self, context: &mut Context, new_label: Option<Label>) {
117 let unique_label = self
118 .get_function(context)
119 .get_unique_label(context, new_label);
120 context.blocks[self.0].label = unique_label;
121 }
122
123 pub fn num_instructions(&self, context: &Context) -> usize {
125 context.blocks[self.0].instructions.len()
126 }
127
128 pub fn get_arg(&self, context: &Context, index: usize) -> Option<Value> {
130 context.blocks[self.0].args.get(index).cloned()
131 }
132
133 pub fn num_predecessors(&self, context: &Context) -> usize {
135 context.blocks[self.0].preds.len()
136 }
137
138 pub fn new_arg(&self, context: &mut Context, ty: Type) -> usize {
140 let idx = context.blocks[self.0].args.len();
141 let arg_val = Value::new_argument(
142 context,
143 BlockArgument {
144 block: *self,
145 idx,
146 ty,
147 },
148 );
149 context.blocks[self.0].args.push(arg_val);
150 idx
151 }
152
153 pub fn set_arg(&self, context: &mut Context, arg: Value) {
154 match context.values[arg.0].value {
155 ValueDatum::Argument(BlockArgument { block, idx, ty: _ })
156 if block == *self && idx < context.blocks[self.0].args.len() =>
157 {
158 context.blocks[self.0].args[idx] = arg;
159 }
160 _ => panic!("Inconsistent block argument being set"),
161 }
162 }
163
164 pub fn add_arg(&self, context: &mut Context, arg: Value) {
166 match context.values[arg.0].value {
167 ValueDatum::Argument(BlockArgument { block, idx, ty: _ })
168 if block == *self && idx == context.blocks[self.0].args.len() =>
169 {
170 context.blocks[self.0].args.push(arg);
171 }
172 _ => panic!("Inconsistent block argument being added"),
173 }
174 }
175
176 pub fn arg_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Value> {
178 context.blocks[self.0].args.iter()
179 }
180
181 pub fn num_args(&self, context: &Context) -> usize {
183 context.blocks[self.0].args.len()
184 }
185
186 pub fn pred_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Block> {
188 context.blocks[self.0].preds.iter()
189 }
190
191 pub fn add_pred(&self, context: &mut Context, from_block: &Block) {
193 context.blocks[self.0].preds.insert(*from_block);
194 }
195
196 pub fn remove_pred(&self, context: &mut Context, from_block: &Block) {
198 context.blocks[self.0].preds.remove(from_block);
199 }
200
201 pub fn replace_pred(&self, context: &mut Context, old_source: &Block, new_source: &Block) {
203 self.remove_pred(context, old_source);
204 self.add_pred(context, new_source);
205 }
206
207 pub fn get_instruction_at(&self, context: &Context, pos: usize) -> Option<Value> {
211 context.blocks[self.0].instructions.get(pos).cloned()
212 }
213
214 pub fn get_terminator<'a>(&self, context: &'a Context) -> Option<&'a Instruction> {
219 context.blocks[self.0].instructions.last().and_then(|val| {
220 if let ValueDatum::Instruction(term_inst) = &context.values[val.0].value {
222 if term_inst.op.is_terminator() {
223 Some(term_inst)
224 } else {
225 None
226 }
227 } else {
228 None
229 }
230 })
231 }
232
233 pub fn get_terminator_mut<'a>(&self, context: &'a mut Context) -> Option<&'a mut Instruction> {
238 context.blocks[self.0].instructions.last().and_then(|val| {
239 if let ValueDatum::Instruction(term_inst) = &mut context.values[val.0].value {
241 if term_inst.op.is_terminator() {
242 Some(term_inst)
243 } else {
244 None
245 }
246 } else {
247 None
248 }
249 })
250 }
251
252 pub(super) fn successors<'a>(&'a self, context: &'a Context) -> Vec<BranchToWithArgs> {
254 match self.get_terminator(context) {
255 Some(Instruction {
256 op:
257 InstOp::ConditionalBranch {
258 true_block,
259 false_block,
260 ..
261 },
262 ..
263 }) => vec![true_block.clone(), false_block.clone()],
264
265 Some(Instruction {
266 op: InstOp::Branch(block),
267 ..
268 }) => vec![block.clone()],
269
270 _otherwise => Vec::new(),
271 }
272 }
273
274 pub fn get_succ_params(&self, context: &Context, succ: &Block) -> Vec<Value> {
276 self.successors(context)
277 .iter()
278 .find(|branch| &branch.block == succ)
279 .map_or(vec![], |branch| branch.args.clone())
280 }
281
282 pub fn get_succ_params_mut<'a>(
284 &'a self,
285 context: &'a mut Context,
286 succ: &Block,
287 ) -> Option<&'a mut Vec<Value>> {
288 match self.get_terminator_mut(context) {
289 Some(Instruction {
290 op:
291 InstOp::ConditionalBranch {
292 true_block,
293 false_block,
294 ..
295 },
296 ..
297 }) => {
298 if true_block.block == *succ {
299 Some(&mut true_block.args)
300 } else if false_block.block == *succ {
301 Some(&mut false_block.args)
302 } else {
303 None
304 }
305 }
306 Some(Instruction {
307 op: InstOp::Branch(block),
308 ..
309 }) if block.block == *succ => Some(&mut block.args),
310 _ => None,
311 }
312 }
313
314 pub(super) fn replace_successor(
317 &self,
318 context: &mut Context,
319 old_succ: Block,
320 new_succ: Block,
321 new_params: Vec<Value>,
322 ) {
323 let mut modified = false;
324 if let Some(term) = self.get_terminator_mut(context) {
325 match term {
326 Instruction {
327 op:
328 InstOp::ConditionalBranch {
329 true_block:
330 BranchToWithArgs {
331 block: true_block,
332 args: true_opds,
333 },
334 false_block:
335 BranchToWithArgs {
336 block: false_block,
337 args: false_opds,
338 },
339 cond_value: _,
340 },
341 ..
342 } => {
343 if old_succ == *true_block {
344 modified = true;
345 *true_block = new_succ;
346 true_opds.clone_from(&new_params);
347 }
348 if old_succ == *false_block {
349 modified = true;
350 *false_block = new_succ;
351 *false_opds = new_params
352 }
353 }
354
355 Instruction {
356 op: InstOp::Branch(BranchToWithArgs { block, args }),
357 ..
358 } if *block == old_succ => {
359 *block = new_succ;
360 *args = new_params;
361 modified = true;
362 }
363 _ => (),
364 }
365 }
366 if modified {
367 old_succ.remove_pred(context, self);
368 new_succ.add_pred(context, self);
369 }
370 }
371
372 pub fn is_terminated_by_return_or_revert(&self, context: &Context) -> bool {
377 self.get_terminator(context).is_some_and(|i| {
378 matches!(
379 i,
380 Instruction {
381 op: InstOp::Ret(..)
382 | InstOp::FuelVm(
383 FuelVmInstruction::Revert(..)
384 | FuelVmInstruction::JmpMem
385 | FuelVmInstruction::Retd { .. }
386 ),
387 ..
388 }
389 )
390 })
391 }
392
393 pub fn replace_values(&self, context: &mut Context, replace_map: &FxHashMap<Value, Value>) {
398 for ins_idx in 0..context.blocks[self.0].instructions.len() {
399 let ins = context.blocks[self.0].instructions[ins_idx];
400 ins.replace_instruction_values(context, replace_map);
401 }
402 }
403
404 pub fn remove_instruction(&self, context: &mut Context, instr_val: Value) {
411 let ins = &mut context.blocks[self.0].instructions;
412 if let Some(pos) = ins.iter().position(|iv| *iv == instr_val) {
413 ins.remove(pos);
414 }
415 }
416
417 pub fn remove_instruction_at(&self, context: &mut Context, pos: usize) {
424 context.blocks[self.0].instructions.remove(pos);
425 }
426
427 pub fn remove_last_instruction(&self, context: &mut Context) {
431 context.blocks[self.0].instructions.pop();
432 }
433
434 pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
436 let ins = &mut context.blocks[self.0].instructions;
437 ins.retain(|value| !pred(*value));
438 }
439
440 pub fn take_body(&self, context: &mut Context, new_insts: Vec<Value>) {
442 let _ = std::mem::replace(&mut (context.blocks[self.0].instructions), new_insts);
443 for inst in &context.blocks[self.0].instructions {
444 let ValueDatum::Instruction(inst) = &mut context.values[inst.0].value else {
445 continue;
446 };
447 inst.parent = *self;
448 }
449 }
450
451 pub fn prepend_instructions(&self, context: &mut Context, mut insts: Vec<Value>) {
453 let block_ins = &mut context.blocks[self.0].instructions;
454 insts.append(block_ins);
455 context.blocks[self.0].instructions = insts;
456 }
457
458 pub fn replace_instruction(
462 &self,
463 context: &mut Context,
464 old_instr_val: Value,
465 new_instr_val: Value,
466 replace_uses: bool,
467 ) -> Result<(), IrError> {
468 match context.blocks[self.0]
469 .instructions
470 .iter_mut()
471 .find(|instr_val| *instr_val == &old_instr_val)
472 {
473 None => Err(IrError::ValueNotFound(
474 "Attempting to replace instruction.".to_owned(),
475 )),
476 Some(instr_val) => {
477 *instr_val = new_instr_val;
478 if replace_uses {
479 self.get_function(context).replace_value(
480 context,
481 old_instr_val,
482 new_instr_val,
483 Some(*self),
484 );
485 }
486 Ok(())
487 }
488 }
489 }
490
491 pub fn split_at(&self, context: &mut Context, split_idx: usize) -> (Block, Block) {
496 let function = context.blocks[self.0].function;
497 if split_idx == 0 {
498 let new_block_name = (*self == self.get_function(context).get_entry_block(context))
504 .then(|| {
505 self.set_label(context, None);
506 "entry".to_owned()
507 });
508 let new_block = function
509 .create_block_before(context, self, new_block_name)
510 .unwrap();
511
512 #[allow(clippy::needless_collect)]
514 let args: Vec<_> = self.arg_iter(context).copied().collect();
515 for arg in args.into_iter() {
516 match &mut context.values[arg.0].value {
517 ValueDatum::Argument(BlockArgument {
518 block,
519 idx: _,
520 ty: _,
521 }) => {
522 *block = new_block;
524 }
525 _ => unreachable!("Block arg value inconsistent"),
526 }
527 new_block.add_arg(context, arg);
528 }
529 context.blocks[self.0].args.clear();
530
531 (new_block, *self)
532 } else {
533 let new_block = function.create_block_after(context, self, None).unwrap();
536
537 let mut tail_instructions = context.blocks[self.0].instructions.split_off(split_idx);
539 for instr in &tail_instructions {
541 instr.get_instruction_mut(context).unwrap().parent = new_block;
542 }
543 context.blocks[new_block.0]
544 .instructions
545 .append(&mut tail_instructions);
546
547 for to_block in match new_block.get_terminator(context) {
553 Some(Instruction {
554 op: InstOp::Branch(to_block),
555 ..
556 }) => {
557 vec![to_block.block]
558 }
559 Some(Instruction {
560 op:
561 InstOp::ConditionalBranch {
562 true_block,
563 false_block,
564 ..
565 },
566 ..
567 }) => {
568 vec![true_block.block, false_block.block]
569 }
570
571 _ => Vec::new(),
572 } {
573 to_block.replace_pred(context, self, &new_block);
574 }
575
576 (*self, new_block)
577 }
578 }
579
580 pub fn instruction_iter(&self, context: &Context) -> InstructionIterator {
582 InstructionIterator::new(context, self)
583 }
584}
585
586pub struct BlockIterator {
588 blocks: Vec<slotmap::DefaultKey>,
589 next: usize,
590}
591
592impl BlockIterator {
593 pub fn new(context: &Context, function: &Function) -> Self {
595 BlockIterator {
598 blocks: context.functions[function.0]
599 .blocks
600 .iter()
601 .map(|block| block.0)
602 .collect(),
603 next: 0,
604 }
605 }
606}
607
608impl Iterator for BlockIterator {
609 type Item = Block;
610
611 fn next(&mut self) -> Option<Block> {
612 if self.next < self.blocks.len() {
613 let idx = self.next;
614 self.next += 1;
615 Some(Block(self.blocks[idx]))
616 } else {
617 None
618 }
619 }
620}