1use std::ops::Not;
2
3use crate::{
15 asm::AsmArg, AnalysisResults, BinaryOpKind, Constant, ConstantContent, Context,
16 FuelVmInstruction, Function, InstOp, InstructionInserter, IrError, Pass, PassMutability,
17 Predicate, ScopedPass, Type, UnaryOpKind, Value,
18};
19
20use rustc_hash::FxHashMap;
21
22pub const MISC_DEMOTION_NAME: &str = "misc-demotion";
23
24pub fn create_misc_demotion_pass() -> Pass {
25 Pass {
26 name: MISC_DEMOTION_NAME,
27 descr: "Miscellaneous by-value demotions to by-reference",
28 deps: Vec::new(),
29 runner: ScopedPass::FunctionPass(PassMutability::Transform(misc_demotion)),
30 }
31}
32
33pub fn misc_demotion(
34 context: &mut Context,
35 _: &AnalysisResults,
36 function: Function,
37) -> Result<bool, IrError> {
38 let log_res = log_demotion(context, function)?;
39 let asm_arg_res = asm_block_arg_demotion(context, function)?;
40 let asm_ret_res = asm_block_ret_demotion(context, function)?;
41 let addrof_res = ptr_to_int_demotion(context, function)?;
42
43 let wide_binary_op_res = wide_binary_op_demotion(context, function)?;
44 let wide_shifts_op_res = wide_shift_op_demotion(context, function)?;
45 let wide_cmp_res = wide_cmp_demotion(context, function)?;
46 let wide_unary_op_res = wide_unary_op_demotion(context, function)?;
47
48 Ok(log_res
49 || asm_arg_res
50 || asm_ret_res
51 || addrof_res
52 || wide_unary_op_res
53 || wide_binary_op_res
54 || wide_shifts_op_res
55 || wide_cmp_res)
56}
57
58fn log_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
59 let candidates = function
61 .instruction_iter(context)
62 .filter_map(|(block, instr_val)| {
63 instr_val.get_instruction(context).and_then(|instr| {
64 if let InstOp::FuelVm(FuelVmInstruction::Log {
66 log_val,
67 log_ty,
68 log_id,
69 }) = instr.op
70 {
71 super::target_fuel::is_demotable_type(context, &log_ty)
72 .then_some((block, instr_val, log_val, log_ty, log_id))
73 } else {
74 None
75 }
76 })
77 })
78 .collect::<Vec<_>>();
79
80 if candidates.is_empty() {
81 return Ok(false);
82 }
83
84 for (block, log_instr_val, logged_val, logged_ty, log_id_val) in candidates {
87 let loc_var =
89 function.new_unique_local_var(context, "__log_arg".to_owned(), logged_ty, None, false);
90 let get_loc_val = Value::new_instruction(context, block, InstOp::GetLocal(loc_var));
91 let store_val = Value::new_instruction(
92 context,
93 block,
94 InstOp::Store {
95 dst_val_ptr: get_loc_val,
96 stored_val: logged_val,
97 },
98 );
99
100 let ptr_ty = Type::new_ptr(context, logged_ty);
102 let new_log_instr_val = Value::new_instruction(
103 context,
104 block,
105 InstOp::FuelVm(FuelVmInstruction::Log {
106 log_val: get_loc_val,
107 log_ty: ptr_ty,
108 log_id: log_id_val,
109 }),
110 );
111
112 block
115 .replace_instruction(context, log_instr_val, new_log_instr_val, false)
116 .unwrap();
117
118 let mut inserter = InstructionInserter::new(
120 context,
121 block,
122 crate::InsertionPosition::Before(new_log_instr_val),
123 );
124 inserter.insert_slice(&[get_loc_val, store_val]);
125 }
126
127 Ok(true)
128}
129
130fn asm_block_arg_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
131 let candidates = function
133 .instruction_iter(context)
134 .filter_map(|(block, instr_val)| {
135 instr_val.get_instruction(context).and_then(|instr| {
136 if let InstOp::AsmBlock(_asm_block, args) = &instr.op {
138 let ref_args = args
139 .iter()
140 .filter_map(
141 |AsmArg {
142 name: _,
143 initializer,
144 }| {
145 initializer.and_then(|init_val| {
146 init_val.get_type(context).and_then(|ty| {
147 super::target_fuel::is_demotable_type(context, &ty)
148 .then_some((init_val, ty))
149 })
150 })
151 },
152 )
153 .collect::<Vec<_>>();
154
155 (!ref_args.is_empty()).then_some((block, instr_val, ref_args))
156 } else {
157 None
158 }
159 })
160 })
161 .collect::<Vec<_>>();
162
163 if candidates.is_empty() {
164 return Ok(false);
165 }
166
167 for (block, asm_block_instr_val, ref_args) in candidates {
168 let mut replace_map = FxHashMap::default();
169 let mut temporaries = Vec::new();
170
171 for (ref_arg_val, ref_arg_ty) in ref_args {
172 let loc_var = function.new_unique_local_var(
174 context,
175 "__asm_arg".to_owned(),
176 ref_arg_ty,
177 None,
178 false,
179 );
180
181 let get_loc_val = Value::new_instruction(context, block, InstOp::GetLocal(loc_var));
183 let store_val = Value::new_instruction(
184 context,
185 block,
186 InstOp::Store {
187 dst_val_ptr: get_loc_val,
188 stored_val: ref_arg_val,
189 },
190 );
191
192 replace_map.insert(ref_arg_val, get_loc_val);
193 temporaries.push(get_loc_val);
194 temporaries.push(store_val);
195 }
196
197 let mut inserter = InstructionInserter::new(
199 context,
200 block,
201 crate::InsertionPosition::Before(asm_block_instr_val),
202 );
203 inserter.insert_slice(&temporaries);
204
205 asm_block_instr_val.replace_instruction_values(context, &replace_map);
207 }
208
209 Ok(true)
210}
211
212fn asm_block_ret_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
213 let candidates = function
215 .instruction_iter(context)
216 .filter_map(|(block, instr_val)| {
217 instr_val.get_instruction(context).and_then(|instr| {
218 if let InstOp::AsmBlock(asm_block, args) = &instr.op {
220 let ret_ty = asm_block.return_type;
221 super::target_fuel::is_demotable_type(context, &ret_ty).then_some((
222 block,
223 instr_val,
224 asm_block.clone(),
225 args.clone(),
226 ret_ty,
227 ))
228 } else {
229 None
230 }
231 })
232 })
233 .collect::<Vec<_>>();
234
235 if candidates.is_empty() {
236 return Ok(false);
237 }
238
239 let mut replace_map = FxHashMap::default();
240 for (block, asm_block_instr_val, mut asm_block, asm_args, ret_ty) in candidates {
241 let ret_ptr_ty = Type::new_ptr(context, ret_ty);
243 asm_block.return_type = ret_ptr_ty;
244 let new_asm_block =
245 Value::new_instruction(context, block, InstOp::AsmBlock(asm_block, asm_args));
246
247 let load_val = Value::new_instruction(context, block, InstOp::Load(new_asm_block));
249 block
250 .replace_instruction(context, asm_block_instr_val, new_asm_block, false)
251 .unwrap();
252 let mut inserter = InstructionInserter::new(
253 context,
254 block,
255 crate::InsertionPosition::After(new_asm_block),
256 );
257 inserter.insert(load_val);
258
259 replace_map.insert(asm_block_instr_val, load_val);
261 }
262 function.replace_values(context, &replace_map, None);
263
264 Ok(true)
265}
266
267fn ptr_to_int_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
268 let candidates = function
270 .instruction_iter(context)
271 .filter_map(|(block, instr_val)| {
272 instr_val.get_instruction(context).and_then(|instr| {
273 if let InstOp::PtrToInt(ptr_val, _int_ty) = instr.op {
275 ptr_val.get_type(context).and_then(|ptr_ty| {
276 super::target_fuel::is_demotable_type(context, &ptr_ty)
277 .then_some((block, instr_val, ptr_val, ptr_ty))
278 })
279 } else {
280 None
281 }
282 })
283 })
284 .collect::<Vec<_>>();
285
286 if candidates.is_empty() {
287 return Ok(false);
288 }
289
290 for (block, ptr_to_int_instr_val, ptr_val, ptr_ty) in candidates {
291 if let Some(instr) = ptr_val.get_instruction(context) {
293 if let Some(loaded_val) = match instr.op {
294 InstOp::Load(loaded_val) => Some(loaded_val),
295 _ => None,
296 } {
297 ptr_to_int_instr_val.replace_instruction_value(context, ptr_val, loaded_val);
298 continue;
299 }
300 }
301
302 let loc_var = function.new_unique_local_var(
307 context,
308 "__ptr_to_int_arg".to_owned(),
309 ptr_ty,
310 None,
311 false,
312 );
313 let get_loc_val = Value::new_instruction(context, block, InstOp::GetLocal(loc_var));
314 let store_val = Value::new_instruction(
315 context,
316 block,
317 InstOp::Store {
318 dst_val_ptr: get_loc_val,
319 stored_val: ptr_val,
320 },
321 );
322
323 let mut inserter = InstructionInserter::new(
325 context,
326 block,
327 crate::InsertionPosition::Before(ptr_to_int_instr_val),
328 );
329 inserter.insert_slice(&[get_loc_val, store_val]);
330
331 ptr_to_int_instr_val.replace_instruction_value(context, ptr_val, get_loc_val);
333 }
334
335 Ok(true)
336}
337
338fn wide_binary_op_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
342 let candidates = function
344 .instruction_iter(context)
345 .filter_map(|(block, instr_val)| {
346 use BinaryOpKind as B;
347 let InstOp::BinaryOp {
348 op: B::Add | B::Sub | B::Mul | B::Div | B::Mod | B::And | B::Or | B::Xor,
349 arg1,
350 arg2,
351 } = instr_val.get_instruction(context)?.op
352 else {
353 return None;
354 };
355
356 let arg1_type = arg1.get_type(context);
357 let arg2_type = arg2.get_type(context);
358
359 match (arg1_type, arg2_type) {
360 (Some(arg1_type), Some(arg2_type))
361 if arg1_type.is_uint_of(context, 256) && arg2_type.is_uint_of(context, 256) =>
362 {
363 Some((block, instr_val))
364 }
365 (Some(arg1_type), Some(arg2_type))
366 if arg1_type.is_b256(context) && arg2_type.is_b256(context) =>
367 {
368 Some((block, instr_val))
369 }
370 _ => None,
371 }
372 })
373 .collect::<Vec<_>>();
374
375 if candidates.is_empty() {
376 return Ok(false);
377 }
378
379 for (block, binary_op_instr_val) in candidates {
383 let InstOp::BinaryOp { op, arg1, arg2 } = binary_op_instr_val
384 .get_instruction(context)
385 .cloned()
386 .unwrap()
387 .op
388 else {
389 continue;
390 };
391
392 let binary_op_metadata = binary_op_instr_val.get_metadata(context);
393
394 let arg1_ty = arg1.get_type(context).unwrap();
395 let arg1_metadata = arg1.get_metadata(context);
396 let arg2_ty = arg2.get_type(context).unwrap();
397 let arg2_metadata = arg2.get_metadata(context);
398
399 let operand_ty = arg1.get_type(context).unwrap();
400
401 let result_local = function.new_unique_local_var(
402 context,
403 "__wide_result".to_owned(),
404 operand_ty,
405 None,
406 true,
407 );
408 let get_result_local =
409 Value::new_instruction(context, block, InstOp::GetLocal(result_local))
410 .add_metadatum(context, binary_op_metadata);
411 let load_result_local =
412 Value::new_instruction(context, block, InstOp::Load(get_result_local))
413 .add_metadatum(context, binary_op_metadata);
414
415 let lhs_store = if !arg1_ty.is_ptr(context) {
417 let lhs_local = function.new_unique_local_var(
418 context,
419 "__wide_lhs".to_owned(),
420 operand_ty,
421 None,
422 false,
423 );
424 let get_lhs_local = Value::new_instruction(context, block, InstOp::GetLocal(lhs_local))
425 .add_metadatum(context, arg1_metadata);
426 let store_lhs_local = Value::new_instruction(
427 context,
428 block,
429 InstOp::Store {
430 dst_val_ptr: get_lhs_local,
431 stored_val: arg1,
432 },
433 )
434 .add_metadatum(context, arg1_metadata);
435 Some((get_lhs_local, store_lhs_local))
436 } else {
437 None
438 };
439
440 let (arg1_needs_insert, get_arg1) = if let Some((lhs_local, _)) = &lhs_store {
441 (false, *lhs_local)
442 } else {
443 (true, arg1)
444 };
445
446 let rhs_store = if !arg2_ty.is_ptr(context) {
448 let rhs_local = function.new_unique_local_var(
449 context,
450 "__wide_rhs".to_owned(),
451 operand_ty,
452 None,
453 false,
454 );
455 let get_rhs_local = Value::new_instruction(context, block, InstOp::GetLocal(rhs_local))
456 .add_metadatum(context, arg2_metadata);
457 let store_lhs_local = Value::new_instruction(
458 context,
459 block,
460 InstOp::Store {
461 dst_val_ptr: get_rhs_local,
462 stored_val: arg2,
463 },
464 )
465 .add_metadatum(context, arg2_metadata);
466 Some((get_rhs_local, store_lhs_local))
467 } else {
468 None
469 };
470
471 let (arg2_needs_insert, get_arg2) = if let Some((rhs_local, _)) = &rhs_store {
472 (false, *rhs_local)
473 } else {
474 (true, arg2)
475 };
476
477 let (wide_op, get_local_zero) = match op {
479 BinaryOpKind::Mod => {
480 let initializer = ConstantContent::new_uint(context, 256, 0);
481 let initializer = Constant::unique(context, initializer);
482 let local_zero = function.new_unique_local_var(
483 context,
484 "__wide_zero".to_owned(),
485 operand_ty,
486 Some(initializer),
487 true,
488 );
489 let get_local_zero =
490 Value::new_instruction(context, block, InstOp::GetLocal(local_zero))
491 .add_metadatum(context, binary_op_metadata);
492
493 (
494 Value::new_instruction(
495 context,
496 block,
497 InstOp::FuelVm(FuelVmInstruction::WideModularOp {
498 op,
499 result: get_result_local,
500 arg1: get_arg1,
501 arg2: get_local_zero,
502 arg3: get_arg2,
503 }),
504 )
505 .add_metadatum(context, binary_op_metadata),
506 Some(get_local_zero),
507 )
508 }
509 _ => (
510 Value::new_instruction(
511 context,
512 block,
513 InstOp::FuelVm(FuelVmInstruction::WideBinaryOp {
514 op,
515 arg1: get_arg1,
516 arg2: get_arg2,
517 result: get_result_local,
518 }),
519 )
520 .add_metadatum(context, binary_op_metadata),
521 None,
522 ),
523 };
524
525 assert!(get_arg1.get_type(context).unwrap().is_ptr(context));
527 assert!(get_arg2.get_type(context).unwrap().is_ptr(context));
528 assert!(get_result_local.get_type(context).unwrap().is_ptr(context));
529 if let Some(get_local_zero) = &get_local_zero {
530 assert!(get_local_zero.get_type(context).unwrap().is_ptr(context));
531 }
532
533 block
534 .replace_instruction(context, binary_op_instr_val, load_result_local, true)
535 .unwrap();
536
537 let mut additional_instrs = Vec::new();
538
539 if let Some((get_lhs_local, store_lhs_local)) = lhs_store {
541 additional_instrs.push(get_lhs_local);
542 additional_instrs.push(store_lhs_local);
543 }
544 if let Some(get_local_zero) = get_local_zero {
546 additional_instrs.push(get_local_zero);
547 }
548
549 if let Some((get_rhs_local, store_rhs_local)) = rhs_store {
551 additional_instrs.push(get_rhs_local);
552 additional_instrs.push(store_rhs_local);
553 }
554 if arg1_needs_insert {
555 additional_instrs.push(get_arg1);
556 }
557
558 if arg2_needs_insert {
559 additional_instrs.push(get_arg2);
560 }
561
562 additional_instrs.push(get_result_local);
563 additional_instrs.push(wide_op);
564
565 let mut inserter = InstructionInserter::new(
566 context,
567 block,
568 crate::InsertionPosition::Before(load_result_local),
569 );
570 inserter.insert_slice(&additional_instrs);
571 }
572
573 Ok(true)
574}
575
576fn wide_cmp_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
580 let candidates = function
582 .instruction_iter(context)
583 .filter_map(|(block, instr_val)| {
584 let InstOp::Cmp(
585 Predicate::Equal | Predicate::LessThan | Predicate::GreaterThan,
586 arg1,
587 arg2,
588 ) = instr_val.get_instruction(context)?.op
589 else {
590 return None;
591 };
592
593 let arg1_type = arg1.get_type(context);
594 let arg2_type = arg2.get_type(context);
595
596 match (arg1_type, arg2_type) {
597 (Some(arg1_type), Some(arg2_type))
598 if arg1_type.is_uint_of(context, 256) && arg2_type.is_uint_of(context, 256) =>
599 {
600 Some((block, instr_val))
601 }
602 (Some(arg1_type), Some(arg2_type))
603 if arg1_type.is_b256(context) && arg2_type.is_b256(context) =>
604 {
605 Some((block, instr_val))
606 }
607 _ => None,
608 }
609 })
610 .collect::<Vec<_>>();
611
612 if candidates.is_empty() {
613 return Ok(false);
614 }
615
616 for (block, cmp_instr_val) in candidates {
618 let InstOp::Cmp(op, arg1, arg2) =
619 cmp_instr_val.get_instruction(context).cloned().unwrap().op
620 else {
621 continue;
622 };
623
624 let cmp_op_metadata = cmp_instr_val.get_metadata(context);
625
626 let arg1_ty = arg1.get_type(context).unwrap();
627 let arg1_metadata = arg1.get_metadata(context);
628 let arg2_ty = arg2.get_type(context).unwrap();
629 let arg2_metadata = arg2.get_metadata(context);
630
631 let lhs_store = arg1_ty.is_ptr(context).not().then(|| {
633 let lhs_local = function.new_unique_local_var(
634 context,
635 "__wide_lhs".to_owned(),
636 arg1_ty,
637 None,
638 false,
639 );
640 let get_lhs_local = Value::new_instruction(context, block, InstOp::GetLocal(lhs_local))
641 .add_metadatum(context, arg1_metadata);
642 let store_lhs_local = Value::new_instruction(
643 context,
644 block,
645 InstOp::Store {
646 dst_val_ptr: get_lhs_local,
647 stored_val: arg1,
648 },
649 )
650 .add_metadatum(context, arg1_metadata);
651 (get_lhs_local, store_lhs_local)
652 });
653
654 let (arg1_needs_insert, get_arg1) = if let Some((lhs_local, _)) = &lhs_store {
655 (false, *lhs_local)
656 } else {
657 (true, arg1)
658 };
659
660 let rhs_store = arg2_ty.is_ptr(context).not().then(|| {
662 let rhs_local = function.new_unique_local_var(
663 context,
664 "__wide_rhs".to_owned(),
665 arg1_ty,
666 None,
667 false,
668 );
669 let get_rhs_local = Value::new_instruction(context, block, InstOp::GetLocal(rhs_local))
670 .add_metadatum(context, arg2_metadata);
671 let store_lhs_local = Value::new_instruction(
672 context,
673 block,
674 InstOp::Store {
675 dst_val_ptr: get_rhs_local,
676 stored_val: arg2,
677 },
678 )
679 .add_metadatum(context, arg2_metadata);
680 (get_rhs_local, store_lhs_local)
681 });
682
683 let (arg2_needs_insert, get_arg2) = if let Some((rhs_local, _)) = &rhs_store {
684 (false, *rhs_local)
685 } else {
686 (true, arg2)
687 };
688
689 assert!(get_arg1.get_type(context).unwrap().is_ptr(context));
691 assert!(get_arg2.get_type(context).unwrap().is_ptr(context));
692
693 let wide_op = Value::new_instruction(
694 context,
695 block,
696 InstOp::FuelVm(FuelVmInstruction::WideCmpOp {
697 op,
698 arg1: get_arg1,
699 arg2: get_arg2,
700 }),
701 )
702 .add_metadatum(context, cmp_op_metadata);
703
704 block
705 .replace_instruction(context, cmp_instr_val, wide_op, true)
706 .unwrap();
707
708 let mut additional_instrs = Vec::new();
709
710 if let Some((get_lhs_local, store_lhs_local)) = lhs_store {
712 additional_instrs.push(get_lhs_local);
713 additional_instrs.push(store_lhs_local);
714 }
715
716 if let Some((get_rhs_local, store_rhs_local)) = rhs_store {
718 additional_instrs.push(get_rhs_local);
719 additional_instrs.push(store_rhs_local);
720 }
721
722 if arg1_needs_insert {
723 additional_instrs.push(get_arg1);
724 }
725
726 if arg2_needs_insert {
727 additional_instrs.push(get_arg2);
728 }
729
730 let mut inserter =
731 InstructionInserter::new(context, block, crate::InsertionPosition::Before(wide_op));
732 inserter.insert_slice(&additional_instrs);
733 }
734
735 Ok(true)
736}
737
738fn wide_unary_op_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
742 let candidates = function
744 .instruction_iter(context)
745 .filter_map(|(block, instr_val)| {
746 let InstOp::UnaryOp {
747 op: UnaryOpKind::Not,
748 arg,
749 } = instr_val.get_instruction(context)?.op
750 else {
751 return None;
752 };
753
754 match arg.get_type(context) {
755 Some(t) if t.is_uint_of(context, 256) || t.is_b256(context) => {
756 Some((block, instr_val))
757 }
758 _ => None,
759 }
760 })
761 .collect::<Vec<_>>();
762
763 if candidates.is_empty() {
764 return Ok(false);
765 }
766
767 for (block, binary_op_instr_val) in candidates {
771 let InstOp::UnaryOp { arg, .. } = binary_op_instr_val
772 .get_instruction(context)
773 .cloned()
774 .unwrap()
775 .op
776 else {
777 continue;
778 };
779
780 let unary_op_metadata = binary_op_instr_val.get_metadata(context);
781
782 let arg_ty = arg.get_type(context).unwrap();
783 let arg_metadata = arg.get_metadata(context);
784
785 let result_local =
786 function.new_unique_local_var(context, "__wide_result".to_owned(), arg_ty, None, true);
787 let get_result_local =
788 Value::new_instruction(context, block, InstOp::GetLocal(result_local))
789 .add_metadatum(context, unary_op_metadata);
790 let load_result_local =
791 Value::new_instruction(context, block, InstOp::Load(get_result_local))
792 .add_metadatum(context, unary_op_metadata);
793
794 let lhs_store = arg_ty.is_ptr(context).not().then(|| {
796 let lhs_local = function.new_unique_local_var(
797 context,
798 "__wide_lhs".to_owned(),
799 arg_ty,
800 None,
801 false,
802 );
803 let get_lhs_local = Value::new_instruction(context, block, InstOp::GetLocal(lhs_local))
804 .add_metadatum(context, arg_metadata);
805 let store_lhs_local = Value::new_instruction(
806 context,
807 block,
808 InstOp::Store {
809 dst_val_ptr: get_lhs_local,
810 stored_val: arg,
811 },
812 )
813 .add_metadatum(context, arg_metadata);
814 (get_lhs_local, store_lhs_local)
815 });
816
817 let (arg1_needs_insert, get_arg) = if let Some((lhs_local, _)) = &lhs_store {
818 (false, *lhs_local)
819 } else {
820 (true, arg)
821 };
822
823 assert!(get_arg.get_type(context).unwrap().is_ptr(context));
825 assert!(get_result_local.get_type(context).unwrap().is_ptr(context));
826
827 let wide_op = Value::new_instruction(
828 context,
829 block,
830 InstOp::FuelVm(FuelVmInstruction::WideUnaryOp {
831 op: UnaryOpKind::Not,
832 arg: get_arg,
833 result: get_result_local,
834 }),
835 )
836 .add_metadatum(context, unary_op_metadata);
837
838 block
839 .replace_instruction(context, binary_op_instr_val, load_result_local, true)
840 .unwrap();
841
842 let mut additional_instrs = Vec::new();
843
844 if let Some((get_lhs_local, store_lhs_local)) = lhs_store {
846 additional_instrs.push(get_lhs_local);
847 additional_instrs.push(store_lhs_local);
848 }
849
850 if arg1_needs_insert {
851 additional_instrs.push(get_arg);
852 }
853
854 additional_instrs.push(get_result_local);
855 additional_instrs.push(wide_op);
856
857 let mut inserter = InstructionInserter::new(
858 context,
859 block,
860 crate::InsertionPosition::Before(load_result_local),
861 );
862 inserter.insert_slice(&additional_instrs);
863 }
864
865 Ok(true)
866}
867
868fn wide_shift_op_demotion(context: &mut Context, function: Function) -> Result<bool, IrError> {
872 let candidates = function
874 .instruction_iter(context)
875 .filter_map(|(block, instr_val)| {
876 let instr = instr_val.get_instruction(context)?;
877 let InstOp::BinaryOp {
878 op: BinaryOpKind::Lsh | BinaryOpKind::Rsh,
879 arg1,
880 arg2,
881 } = instr.op
882 else {
883 return None;
884 };
885
886 let arg1_type = arg1.get_type(context);
887 let arg2_type = arg2.get_type(context);
888
889 match (arg1_type, arg2_type) {
890 (Some(arg1_type), Some(arg2_type))
891 if arg1_type.is_uint_of(context, 256) && arg2_type.is_uint64(context) =>
892 {
893 Some((block, instr_val))
894 }
895 (Some(arg1_type), Some(arg2_type))
896 if arg1_type.is_b256(context) && arg2_type.is_uint64(context) =>
897 {
898 Some((block, instr_val))
899 }
900 _ => None,
901 }
902 })
903 .collect::<Vec<_>>();
904
905 if candidates.is_empty() {
906 return Ok(false);
907 }
908
909 for (block, binary_op_instr_val) in candidates {
913 let InstOp::BinaryOp { op, arg1, arg2 } = binary_op_instr_val
914 .get_instruction(context)
915 .cloned()
916 .unwrap()
917 .op
918 else {
919 continue;
920 };
921
922 let binary_op_metadata = binary_op_instr_val.get_metadata(context);
923
924 let arg1_ty = arg1.get_type(context).unwrap();
925 let arg1_metadata = arg1.get_metadata(context);
926
927 let arg2_ty = arg2.get_type(context).unwrap();
928
929 let operand_ty = arg1.get_type(context).unwrap();
930
931 let result_local = function.new_unique_local_var(
932 context,
933 "__wide_result".to_owned(),
934 operand_ty,
935 None,
936 true,
937 );
938 let get_result_local =
939 Value::new_instruction(context, block, InstOp::GetLocal(result_local))
940 .add_metadatum(context, binary_op_metadata);
941 let load_result_local =
942 Value::new_instruction(context, block, InstOp::Load(get_result_local))
943 .add_metadatum(context, binary_op_metadata);
944
945 let lhs_store = if !arg1_ty.is_ptr(context) {
947 let lhs_local = function.new_unique_local_var(
948 context,
949 "__wide_lhs".to_owned(),
950 operand_ty,
951 None,
952 false,
953 );
954 let get_lhs_local = Value::new_instruction(context, block, InstOp::GetLocal(lhs_local))
955 .add_metadatum(context, arg1_metadata);
956 let store_lhs_local = Value::new_instruction(
957 context,
958 block,
959 InstOp::Store {
960 dst_val_ptr: get_lhs_local,
961 stored_val: arg1,
962 },
963 )
964 .add_metadatum(context, arg1_metadata);
965 Some((get_lhs_local, store_lhs_local))
966 } else {
967 None
968 };
969
970 let (arg1_needs_insert, get_arg1) = if let Some((lhs_local, _)) = &lhs_store {
971 (false, *lhs_local)
972 } else {
973 (true, arg1)
974 };
975
976 assert!(get_arg1.get_type(context).unwrap().is_ptr(context));
979 assert!(get_result_local.get_type(context).unwrap().is_ptr(context));
980 assert!(arg2_ty.is_uint64(context));
981
982 let wide_op = Value::new_instruction(
983 context,
984 block,
985 InstOp::FuelVm(FuelVmInstruction::WideBinaryOp {
986 op,
987 arg1: get_arg1,
988 arg2,
989 result: get_result_local,
990 }),
991 )
992 .add_metadatum(context, binary_op_metadata);
993
994 block
995 .replace_instruction(context, binary_op_instr_val, load_result_local, true)
996 .unwrap();
997
998 let mut additional_instrs = Vec::new();
999
1000 if let Some((get_lhs_local, store_lhs_local)) = lhs_store {
1002 additional_instrs.push(get_lhs_local);
1003 additional_instrs.push(store_lhs_local);
1004 }
1005
1006 if arg1_needs_insert {
1007 additional_instrs.push(get_arg1);
1008 }
1009
1010 additional_instrs.push(get_result_local);
1011 additional_instrs.push(wide_op);
1012
1013 let mut inserter = InstructionInserter::new(
1014 context,
1015 block,
1016 crate::InsertionPosition::Before(load_result_local),
1017 );
1018 inserter.insert_slice(&additional_instrs);
1019 }
1020
1021 Ok(true)
1022}