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
30#[doc(hidden)]
31pub struct BlockContent {
32 pub label: Label,
34 pub function: Function,
36 pub(crate) instructions: Vec<Value>,
38 pub args: Vec<Value>,
40 pub preds: FxHashSet<Block>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DebugWithContext)]
45pub struct BlockArgument {
46 pub block: Block,
48 pub idx: usize,
50 pub ty: Type,
51 pub is_immutable: bool,
53}
54
55impl BlockArgument {
56 pub fn get_val_coming_from(&self, context: &Context, from_block: &Block) -> Option<Value> {
58 for BranchToWithArgs {
59 block: succ_block,
60 args,
61 } in from_block.successors(context)
62 {
63 if succ_block == self.block {
64 return Some(args[self.idx]);
65 }
66 }
67 None
68 }
69
70 pub fn get_vals_coming_from<'a>(
74 &'a self,
75 context: &'a Context,
76 ) -> impl Iterator<Item = (Block, Value)> + 'a {
77 self.block.pred_iter(context).flat_map(move |pred| {
78 self.get_val_coming_from(context, pred)
79 .map(|value| (*pred, value))
80 })
81 }
82
83 pub fn as_value(&self, context: &Context) -> Value {
85 self.block.get_arg(context, self.idx).unwrap()
86 }
87}
88
89pub type Label = String;
91
92impl Block {
93 pub fn new(context: &mut Context, function: Function, label: Option<String>) -> Block {
98 let label = function.get_unique_label(context, label);
99 let content = BlockContent {
100 label,
101 function,
102 instructions: vec![],
103 args: vec![],
104 preds: FxHashSet::default(),
105 };
106 Block(context.blocks.insert(content))
107 }
108
109 pub fn get_module(&self, context: &Context) -> Module {
111 let f = context.blocks[self.0].function;
112 context.functions[f.0].module
113 }
114
115 pub fn get_function(&self, context: &Context) -> Function {
117 context.blocks[self.0].function
118 }
119
120 pub fn append<'a, 'eng>(
122 &self,
123 context: &'a mut Context<'eng>,
124 ) -> InstructionInserter<'a, 'eng> {
125 InstructionInserter::new(context, *self, crate::InsertionPosition::End)
126 }
127
128 pub fn get_label(&self, context: &Context) -> String {
131 context.blocks[self.0].label.clone()
132 }
133
134 pub fn set_label(&self, context: &mut Context, new_label: Option<Label>) {
136 let unique_label = self
137 .get_function(context)
138 .get_unique_label(context, new_label);
139 context.blocks[self.0].label = unique_label;
140 }
141
142 pub fn num_instructions(&self, context: &Context) -> usize {
144 context.blocks[self.0].instructions.len()
145 }
146
147 pub fn get_arg(&self, context: &Context, index: usize) -> Option<Value> {
149 context.blocks[self.0].args.get(index).cloned()
150 }
151
152 pub fn num_predecessors(&self, context: &Context) -> usize {
154 context.blocks[self.0].preds.len()
155 }
156
157 pub fn new_arg(&self, context: &mut Context, ty: Type) -> usize {
159 let idx = context.blocks[self.0].args.len();
160 let arg_val = Value::new_argument(
161 context,
162 BlockArgument {
163 block: *self,
164 idx,
165 ty,
166 is_immutable: false,
167 },
168 );
169 context.blocks[self.0].args.push(arg_val);
170 idx
171 }
172
173 pub fn set_arg(&self, context: &mut Context, arg: Value) {
174 match context.values[arg.0].value {
175 ValueDatum::Argument(BlockArgument {
176 block,
177 idx,
178 ty: _,
179 is_immutable: _,
180 }) if block == *self && idx < context.blocks[self.0].args.len() => {
181 context.blocks[self.0].args[idx] = arg;
182 }
183 _ => panic!("Inconsistent block argument being set"),
184 }
185 }
186
187 pub fn add_arg(&self, context: &mut Context, arg: Value) {
189 match context.values[arg.0].value {
190 ValueDatum::Argument(BlockArgument {
191 block,
192 idx,
193 ty: _,
194 is_immutable: _,
195 }) if block == *self && idx == context.blocks[self.0].args.len() => {
196 context.blocks[self.0].args.push(arg);
197 }
198 _ => panic!("Inconsistent block argument being added"),
199 }
200 }
201
202 pub fn arg_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Value> {
204 context.blocks[self.0].args.iter()
205 }
206
207 pub fn num_args(&self, context: &Context) -> usize {
209 context.blocks[self.0].args.len()
210 }
211
212 pub fn pred_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Block> {
214 context.blocks[self.0].preds.iter()
215 }
216
217 pub fn add_pred(&self, context: &mut Context, from_block: &Block) {
219 context.blocks[self.0].preds.insert(*from_block);
220 }
221
222 pub fn remove_pred(&self, context: &mut Context, from_block: &Block) {
224 context.blocks[self.0].preds.remove(from_block);
225 }
226
227 pub fn replace_pred(&self, context: &mut Context, old_source: &Block, new_source: &Block) {
229 self.remove_pred(context, old_source);
230 self.add_pred(context, new_source);
231 }
232
233 pub fn get_instruction_at(&self, context: &Context, pos: usize) -> Option<Value> {
237 context.blocks[self.0].instructions.get(pos).cloned()
238 }
239
240 pub fn get_terminator<'a>(&self, context: &'a Context) -> Option<&'a Instruction> {
245 context.blocks[self.0].instructions.last().and_then(|val| {
246 if let ValueDatum::Instruction(term_inst) = &context.values[val.0].value {
248 if term_inst.op.is_terminator() {
249 Some(term_inst)
250 } else {
251 None
252 }
253 } else {
254 None
255 }
256 })
257 }
258
259 pub fn get_terminator_mut<'a>(&self, context: &'a mut Context) -> Option<&'a mut Instruction> {
264 context.blocks[self.0].instructions.last().and_then(|val| {
265 if let ValueDatum::Instruction(term_inst) = &mut context.values[val.0].value {
267 if term_inst.op.is_terminator() {
268 Some(term_inst)
269 } else {
270 None
271 }
272 } else {
273 None
274 }
275 })
276 }
277
278 pub(super) fn successors<'a>(&'a self, context: &'a Context) -> Vec<BranchToWithArgs> {
280 match self.get_terminator(context) {
281 Some(Instruction {
282 op:
283 InstOp::ConditionalBranch {
284 true_block,
285 false_block,
286 ..
287 },
288 ..
289 }) => vec![true_block.clone(), false_block.clone()],
290
291 Some(Instruction {
292 op: InstOp::Branch(block),
293 ..
294 }) => vec![block.clone()],
295
296 _otherwise => Vec::new(),
297 }
298 }
299
300 pub fn get_succ_params(&self, context: &Context, succ: &Block) -> Vec<Value> {
302 self.successors(context)
303 .iter()
304 .find(|branch| &branch.block == succ)
305 .map_or(vec![], |branch| branch.args.clone())
306 }
307
308 pub fn get_succ_params_mut<'a>(
310 &'a self,
311 context: &'a mut Context,
312 succ: &Block,
313 ) -> Option<&'a mut Vec<Value>> {
314 match self.get_terminator_mut(context) {
315 Some(Instruction {
316 op:
317 InstOp::ConditionalBranch {
318 true_block,
319 false_block,
320 ..
321 },
322 ..
323 }) => {
324 if true_block.block == *succ {
325 Some(&mut true_block.args)
326 } else if false_block.block == *succ {
327 Some(&mut false_block.args)
328 } else {
329 None
330 }
331 }
332 Some(Instruction {
333 op: InstOp::Branch(block),
334 ..
335 }) if block.block == *succ => Some(&mut block.args),
336 _ => None,
337 }
338 }
339
340 pub(super) fn replace_successor(
343 &self,
344 context: &mut Context,
345 old_succ: Block,
346 new_succ: Block,
347 new_params: Vec<Value>,
348 ) {
349 let mut modified = false;
350 if let Some(term) = self.get_terminator_mut(context) {
351 match term {
352 Instruction {
353 op:
354 InstOp::ConditionalBranch {
355 true_block:
356 BranchToWithArgs {
357 block: true_block,
358 args: true_opds,
359 },
360 false_block:
361 BranchToWithArgs {
362 block: false_block,
363 args: false_opds,
364 },
365 cond_value: _,
366 },
367 ..
368 } => {
369 if old_succ == *true_block {
370 modified = true;
371 *true_block = new_succ;
372 true_opds.clone_from(&new_params);
373 }
374 if old_succ == *false_block {
375 modified = true;
376 *false_block = new_succ;
377 *false_opds = new_params
378 }
379 }
380
381 Instruction {
382 op: InstOp::Branch(BranchToWithArgs { block, args }),
383 ..
384 } if *block == old_succ => {
385 *block = new_succ;
386 *args = new_params;
387 modified = true;
388 }
389 _ => (),
390 }
391 }
392 if modified {
393 old_succ.remove_pred(context, self);
394 new_succ.add_pred(context, self);
395 }
396 }
397
398 pub fn is_terminated_by_return_or_revert(&self, context: &Context) -> bool {
403 self.get_terminator(context).is_some_and(|i| {
404 matches!(
405 i,
406 Instruction {
407 op: InstOp::Ret(..)
408 | InstOp::FuelVm(
409 FuelVmInstruction::Revert(..)
410 | FuelVmInstruction::JmpMem
411 | FuelVmInstruction::Retd { .. }
412 ),
413 ..
414 }
415 )
416 })
417 }
418
419 pub fn replace_values(&self, context: &mut Context, replace_map: &FxHashMap<Value, Value>) {
424 for ins_idx in 0..context.blocks[self.0].instructions.len() {
425 let ins = context.blocks[self.0].instructions[ins_idx];
426 ins.replace_instruction_values(context, replace_map);
427 }
428 }
429
430 pub fn remove_instruction(&self, context: &mut Context, instr_val: Value) {
437 let ins = &mut context.blocks[self.0].instructions;
438 if let Some(pos) = ins.iter().position(|iv| *iv == instr_val) {
439 ins.remove(pos);
440 }
441 }
442
443 pub fn remove_instruction_at(&self, context: &mut Context, pos: usize) {
450 context.blocks[self.0].instructions.remove(pos);
451 }
452
453 pub fn remove_last_instruction(&self, context: &mut Context) {
457 context.blocks[self.0].instructions.pop();
458 }
459
460 pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
462 let ins = &mut context.blocks[self.0].instructions;
463 ins.retain(|value| !pred(*value));
464 }
465
466 pub fn take_body(&self, context: &mut Context, new_insts: Vec<Value>) {
468 let _ = std::mem::replace(&mut (context.blocks[self.0].instructions), new_insts);
469 for inst in &context.blocks[self.0].instructions {
470 let ValueDatum::Instruction(inst) = &mut context.values[inst.0].value else {
471 continue;
472 };
473 inst.parent = *self;
474 }
475 }
476
477 pub fn prepend_instructions(&self, context: &mut Context, mut insts: Vec<Value>) {
479 let block_ins = &mut context.blocks[self.0].instructions;
480 insts.append(block_ins);
481 context.blocks[self.0].instructions = insts;
482 }
483
484 pub fn replace_instruction(
488 &self,
489 context: &mut Context,
490 old_instr_val: Value,
491 new_instr_val: Value,
492 replace_uses: bool,
493 ) -> Result<(), IrError> {
494 match context.blocks[self.0]
495 .instructions
496 .iter_mut()
497 .find(|instr_val| *instr_val == &old_instr_val)
498 {
499 None => Err(IrError::ValueNotFound(
500 "Attempting to replace instruction.".to_owned(),
501 )),
502 Some(instr_val) => {
503 *instr_val = new_instr_val;
504 if replace_uses {
505 self.get_function(context).replace_value(
506 context,
507 old_instr_val,
508 new_instr_val,
509 Some(*self),
510 );
511 }
512 Ok(())
513 }
514 }
515 }
516
517 pub fn split_at(&self, context: &mut Context, split_idx: usize) -> (Block, Block) {
522 let function = context.blocks[self.0].function;
523 if split_idx == 0 {
524 let new_block_name = (*self == self.get_function(context).get_entry_block(context))
530 .then(|| {
531 self.set_label(context, None);
532 "entry".to_owned()
533 });
534 let new_block = function
535 .create_block_before(context, self, new_block_name)
536 .unwrap();
537
538 #[allow(clippy::needless_collect)]
540 let args: Vec<_> = self.arg_iter(context).copied().collect();
541 for arg in args.into_iter() {
542 match &mut context.values[arg.0].value {
543 ValueDatum::Argument(BlockArgument {
544 block,
545 idx: _,
546 ty: _,
547 is_immutable: _,
548 }) => {
549 *block = new_block;
551 }
552 _ => unreachable!("Block arg value inconsistent"),
553 }
554 new_block.add_arg(context, arg);
555 }
556 context.blocks[self.0].args.clear();
557
558 (new_block, *self)
559 } else {
560 let new_block = function.create_block_after(context, self, None).unwrap();
563
564 let mut tail_instructions = context.blocks[self.0].instructions.split_off(split_idx);
566 for instr in &tail_instructions {
568 instr.get_instruction_mut(context).unwrap().parent = new_block;
569 }
570 context.blocks[new_block.0]
571 .instructions
572 .append(&mut tail_instructions);
573
574 for to_block in match new_block.get_terminator(context) {
580 Some(Instruction {
581 op: InstOp::Branch(to_block),
582 ..
583 }) => {
584 vec![to_block.block]
585 }
586 Some(Instruction {
587 op:
588 InstOp::ConditionalBranch {
589 true_block,
590 false_block,
591 ..
592 },
593 ..
594 }) => {
595 vec![true_block.block, false_block.block]
596 }
597
598 _ => Vec::new(),
599 } {
600 to_block.replace_pred(context, self, &new_block);
601 }
602
603 (*self, new_block)
604 }
605 }
606
607 pub fn instruction_iter(&self, context: &Context) -> InstructionIterator {
609 InstructionIterator::new(context, self)
610 }
611}
612
613pub struct BlockIterator {
615 blocks: Vec<slotmap::DefaultKey>,
616 next: usize,
617}
618
619impl BlockIterator {
620 pub fn new(context: &Context, function: &Function) -> Self {
622 BlockIterator {
625 blocks: context.functions[function.0]
626 .blocks
627 .iter()
628 .map(|block| block.0)
629 .collect(),
630 next: 0,
631 }
632 }
633}
634
635impl Iterator for BlockIterator {
636 type Item = Block;
637
638 fn next(&mut self) -> Option<Block> {
639 if self.next < self.blocks.len() {
640 let idx = self.next;
641 self.next += 1;
642 Some(Block(self.blocks[idx]))
643 } else {
644 None
645 }
646 }
647}