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 pub is_immutable: bool,
60}
61
62impl BlockArgument {
63 pub fn get_val_coming_from(&self, context: &Context, from_block: &Block) -> Option<Value> {
65 for BranchToWithArgs {
66 block: succ_block,
67 args,
68 } in from_block.successors(context)
69 {
70 if succ_block == self.block {
71 return Some(args[self.idx]);
72 }
73 }
74 None
75 }
76
77 pub fn as_value(&self, context: &Context) -> Value {
79 self.block.get_arg(context, self.idx).unwrap()
80 }
81}
82
83pub type Label = String;
85
86impl Block {
87 pub fn new(context: &mut Context, function: Function, label: Option<String>) -> Block {
92 let label = function.get_unique_label(context, label);
93 let content = BlockContent {
94 label,
95 function,
96 instructions: vec![],
97 args: vec![],
98 preds: FxHashSet::default(),
99 };
100 Block(context.blocks.insert(content))
101 }
102
103 pub fn get_function(&self, context: &Context) -> Function {
105 context.blocks[self.0].function
106 }
107
108 pub fn append<'a, 'eng>(
110 &self,
111 context: &'a mut Context<'eng>,
112 ) -> InstructionInserter<'a, 'eng> {
113 InstructionInserter::new(context, *self, crate::InsertionPosition::End)
114 }
115
116 pub fn get_label(&self, context: &Context) -> String {
119 context.blocks[self.0].label.clone()
120 }
121
122 pub fn set_label(&self, context: &mut Context, new_label: Option<Label>) {
124 let unique_label = self
125 .get_function(context)
126 .get_unique_label(context, new_label);
127 context.blocks[self.0].label = unique_label;
128 }
129
130 pub fn num_instructions(&self, context: &Context) -> usize {
132 context.blocks[self.0].instructions.len()
133 }
134
135 pub fn get_arg(&self, context: &Context, index: usize) -> Option<Value> {
137 context.blocks[self.0].args.get(index).cloned()
138 }
139
140 pub fn num_predecessors(&self, context: &Context) -> usize {
142 context.blocks[self.0].preds.len()
143 }
144
145 pub fn new_arg(&self, context: &mut Context, ty: Type) -> usize {
147 let idx = context.blocks[self.0].args.len();
148 let arg_val = Value::new_argument(
149 context,
150 BlockArgument {
151 block: *self,
152 idx,
153 ty,
154 is_immutable: false,
155 },
156 );
157 context.blocks[self.0].args.push(arg_val);
158 idx
159 }
160
161 pub fn set_arg(&self, context: &mut Context, arg: Value) {
162 match context.values[arg.0].value {
163 ValueDatum::Argument(BlockArgument {
164 block,
165 idx,
166 ty: _,
167 is_immutable: _,
168 }) if block == *self && idx < context.blocks[self.0].args.len() => {
169 context.blocks[self.0].args[idx] = arg;
170 }
171 _ => panic!("Inconsistent block argument being set"),
172 }
173 }
174
175 pub fn add_arg(&self, context: &mut Context, arg: Value) {
177 match context.values[arg.0].value {
178 ValueDatum::Argument(BlockArgument {
179 block,
180 idx,
181 ty: _,
182 is_immutable: _,
183 }) if block == *self && idx == context.blocks[self.0].args.len() => {
184 context.blocks[self.0].args.push(arg);
185 }
186 _ => panic!("Inconsistent block argument being added"),
187 }
188 }
189
190 pub fn arg_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Value> {
192 context.blocks[self.0].args.iter()
193 }
194
195 pub fn num_args(&self, context: &Context) -> usize {
197 context.blocks[self.0].args.len()
198 }
199
200 pub fn pred_iter<'a>(&'a self, context: &'a Context) -> impl Iterator<Item = &'a Block> {
202 context.blocks[self.0].preds.iter()
203 }
204
205 pub fn add_pred(&self, context: &mut Context, from_block: &Block) {
207 context.blocks[self.0].preds.insert(*from_block);
208 }
209
210 pub fn remove_pred(&self, context: &mut Context, from_block: &Block) {
212 context.blocks[self.0].preds.remove(from_block);
213 }
214
215 pub fn replace_pred(&self, context: &mut Context, old_source: &Block, new_source: &Block) {
217 self.remove_pred(context, old_source);
218 self.add_pred(context, new_source);
219 }
220
221 pub fn get_instruction_at(&self, context: &Context, pos: usize) -> Option<Value> {
225 context.blocks[self.0].instructions.get(pos).cloned()
226 }
227
228 pub fn get_terminator<'a>(&self, context: &'a Context) -> Option<&'a Instruction> {
233 context.blocks[self.0].instructions.last().and_then(|val| {
234 if let ValueDatum::Instruction(term_inst) = &context.values[val.0].value {
236 if term_inst.op.is_terminator() {
237 Some(term_inst)
238 } else {
239 None
240 }
241 } else {
242 None
243 }
244 })
245 }
246
247 pub fn get_terminator_mut<'a>(&self, context: &'a mut Context) -> Option<&'a mut Instruction> {
252 context.blocks[self.0].instructions.last().and_then(|val| {
253 if let ValueDatum::Instruction(term_inst) = &mut context.values[val.0].value {
255 if term_inst.op.is_terminator() {
256 Some(term_inst)
257 } else {
258 None
259 }
260 } else {
261 None
262 }
263 })
264 }
265
266 pub(super) fn successors<'a>(&'a self, context: &'a Context) -> Vec<BranchToWithArgs> {
268 match self.get_terminator(context) {
269 Some(Instruction {
270 op:
271 InstOp::ConditionalBranch {
272 true_block,
273 false_block,
274 ..
275 },
276 ..
277 }) => vec![true_block.clone(), false_block.clone()],
278
279 Some(Instruction {
280 op: InstOp::Branch(block),
281 ..
282 }) => vec![block.clone()],
283
284 _otherwise => Vec::new(),
285 }
286 }
287
288 pub fn get_succ_params(&self, context: &Context, succ: &Block) -> Vec<Value> {
290 self.successors(context)
291 .iter()
292 .find(|branch| &branch.block == succ)
293 .map_or(vec![], |branch| branch.args.clone())
294 }
295
296 pub fn get_succ_params_mut<'a>(
298 &'a self,
299 context: &'a mut Context,
300 succ: &Block,
301 ) -> Option<&'a mut Vec<Value>> {
302 match self.get_terminator_mut(context) {
303 Some(Instruction {
304 op:
305 InstOp::ConditionalBranch {
306 true_block,
307 false_block,
308 ..
309 },
310 ..
311 }) => {
312 if true_block.block == *succ {
313 Some(&mut true_block.args)
314 } else if false_block.block == *succ {
315 Some(&mut false_block.args)
316 } else {
317 None
318 }
319 }
320 Some(Instruction {
321 op: InstOp::Branch(block),
322 ..
323 }) if block.block == *succ => Some(&mut block.args),
324 _ => None,
325 }
326 }
327
328 pub(super) fn replace_successor(
331 &self,
332 context: &mut Context,
333 old_succ: Block,
334 new_succ: Block,
335 new_params: Vec<Value>,
336 ) {
337 let mut modified = false;
338 if let Some(term) = self.get_terminator_mut(context) {
339 match term {
340 Instruction {
341 op:
342 InstOp::ConditionalBranch {
343 true_block:
344 BranchToWithArgs {
345 block: true_block,
346 args: true_opds,
347 },
348 false_block:
349 BranchToWithArgs {
350 block: false_block,
351 args: false_opds,
352 },
353 cond_value: _,
354 },
355 ..
356 } => {
357 if old_succ == *true_block {
358 modified = true;
359 *true_block = new_succ;
360 true_opds.clone_from(&new_params);
361 }
362 if old_succ == *false_block {
363 modified = true;
364 *false_block = new_succ;
365 *false_opds = new_params
366 }
367 }
368
369 Instruction {
370 op: InstOp::Branch(BranchToWithArgs { block, args }),
371 ..
372 } if *block == old_succ => {
373 *block = new_succ;
374 *args = new_params;
375 modified = true;
376 }
377 _ => (),
378 }
379 }
380 if modified {
381 old_succ.remove_pred(context, self);
382 new_succ.add_pred(context, self);
383 }
384 }
385
386 pub fn is_terminated_by_return_or_revert(&self, context: &Context) -> bool {
391 self.get_terminator(context).is_some_and(|i| {
392 matches!(
393 i,
394 Instruction {
395 op: InstOp::Ret(..)
396 | InstOp::FuelVm(
397 FuelVmInstruction::Revert(..)
398 | FuelVmInstruction::JmpMem
399 | FuelVmInstruction::Retd { .. }
400 ),
401 ..
402 }
403 )
404 })
405 }
406
407 pub fn replace_values(&self, context: &mut Context, replace_map: &FxHashMap<Value, Value>) {
412 for ins_idx in 0..context.blocks[self.0].instructions.len() {
413 let ins = context.blocks[self.0].instructions[ins_idx];
414 ins.replace_instruction_values(context, replace_map);
415 }
416 }
417
418 pub fn remove_instruction(&self, context: &mut Context, instr_val: Value) {
425 let ins = &mut context.blocks[self.0].instructions;
426 if let Some(pos) = ins.iter().position(|iv| *iv == instr_val) {
427 ins.remove(pos);
428 }
429 }
430
431 pub fn remove_instruction_at(&self, context: &mut Context, pos: usize) {
438 context.blocks[self.0].instructions.remove(pos);
439 }
440
441 pub fn remove_last_instruction(&self, context: &mut Context) {
445 context.blocks[self.0].instructions.pop();
446 }
447
448 pub fn remove_instructions<T: Fn(Value) -> bool>(&self, context: &mut Context, pred: T) {
450 let ins = &mut context.blocks[self.0].instructions;
451 ins.retain(|value| !pred(*value));
452 }
453
454 pub fn take_body(&self, context: &mut Context, new_insts: Vec<Value>) {
456 let _ = std::mem::replace(&mut (context.blocks[self.0].instructions), new_insts);
457 for inst in &context.blocks[self.0].instructions {
458 let ValueDatum::Instruction(inst) = &mut context.values[inst.0].value else {
459 continue;
460 };
461 inst.parent = *self;
462 }
463 }
464
465 pub fn prepend_instructions(&self, context: &mut Context, mut insts: Vec<Value>) {
467 let block_ins = &mut context.blocks[self.0].instructions;
468 insts.append(block_ins);
469 context.blocks[self.0].instructions = insts;
470 }
471
472 pub fn replace_instruction(
476 &self,
477 context: &mut Context,
478 old_instr_val: Value,
479 new_instr_val: Value,
480 replace_uses: bool,
481 ) -> Result<(), IrError> {
482 match context.blocks[self.0]
483 .instructions
484 .iter_mut()
485 .find(|instr_val| *instr_val == &old_instr_val)
486 {
487 None => Err(IrError::ValueNotFound(
488 "Attempting to replace instruction.".to_owned(),
489 )),
490 Some(instr_val) => {
491 *instr_val = new_instr_val;
492 if replace_uses {
493 self.get_function(context).replace_value(
494 context,
495 old_instr_val,
496 new_instr_val,
497 Some(*self),
498 );
499 }
500 Ok(())
501 }
502 }
503 }
504
505 pub fn split_at(&self, context: &mut Context, split_idx: usize) -> (Block, Block) {
510 let function = context.blocks[self.0].function;
511 if split_idx == 0 {
512 let new_block_name = (*self == self.get_function(context).get_entry_block(context))
518 .then(|| {
519 self.set_label(context, None);
520 "entry".to_owned()
521 });
522 let new_block = function
523 .create_block_before(context, self, new_block_name)
524 .unwrap();
525
526 #[allow(clippy::needless_collect)]
528 let args: Vec<_> = self.arg_iter(context).copied().collect();
529 for arg in args.into_iter() {
530 match &mut context.values[arg.0].value {
531 ValueDatum::Argument(BlockArgument {
532 block,
533 idx: _,
534 ty: _,
535 is_immutable: _,
536 }) => {
537 *block = new_block;
539 }
540 _ => unreachable!("Block arg value inconsistent"),
541 }
542 new_block.add_arg(context, arg);
543 }
544 context.blocks[self.0].args.clear();
545
546 (new_block, *self)
547 } else {
548 let new_block = function.create_block_after(context, self, None).unwrap();
551
552 let mut tail_instructions = context.blocks[self.0].instructions.split_off(split_idx);
554 for instr in &tail_instructions {
556 instr.get_instruction_mut(context).unwrap().parent = new_block;
557 }
558 context.blocks[new_block.0]
559 .instructions
560 .append(&mut tail_instructions);
561
562 for to_block in match new_block.get_terminator(context) {
568 Some(Instruction {
569 op: InstOp::Branch(to_block),
570 ..
571 }) => {
572 vec![to_block.block]
573 }
574 Some(Instruction {
575 op:
576 InstOp::ConditionalBranch {
577 true_block,
578 false_block,
579 ..
580 },
581 ..
582 }) => {
583 vec![true_block.block, false_block.block]
584 }
585
586 _ => Vec::new(),
587 } {
588 to_block.replace_pred(context, self, &new_block);
589 }
590
591 (*self, new_block)
592 }
593 }
594
595 pub fn instruction_iter(&self, context: &Context) -> InstructionIterator {
597 InstructionIterator::new(context, self)
598 }
599}
600
601pub struct BlockIterator {
603 blocks: Vec<slotmap::DefaultKey>,
604 next: usize,
605}
606
607impl BlockIterator {
608 pub fn new(context: &Context, function: &Function) -> Self {
610 BlockIterator {
613 blocks: context.functions[function.0]
614 .blocks
615 .iter()
616 .map(|block| block.0)
617 .collect(),
618 next: 0,
619 }
620 }
621}
622
623impl Iterator for BlockIterator {
624 type Item = Block;
625
626 fn next(&mut self) -> Option<Block> {
627 if self.next < self.blocks.len() {
628 let idx = self.next;
629 self.next += 1;
630 Some(Block(self.blocks[idx]))
631 } else {
632 None
633 }
634 }
635}