Skip to content

Commit

Permalink
Improve copy-propagation and DCE for memory operations (FuelLabs#4592)
Browse files Browse the repository at this point in the history
## Description
This PR adds a new copy-propagation for memory operations and also
enhances the DCE pass to eliminate dead stores and `memcpy`s.

Closes FuelLabs#4345. This doesn't do a full data-flow analysis for the function
as mentioned in the Issue description (that would be quite compile-time
heavy). Instead it tracks data-flow (available `memcpy`s per block and
optimizes when safe).

This does not remove the existing ad-hoc copy-propagation for memory
operations (which is now called `local_copy_prop_prememcpy`) yet. More
testing and benchmarking needs to be done before that is removed.

This PR introduces an escaped symbols analysis pass (also used by DCE
now) to figure out symbols (locals / args) that escape out of our
purview, and hence deemed unsafe to touch for any optimization.

Code size reduction with this PR is tabulated below. (note that the gtf
intrinsic test code size has increased, that's because the test itself
has been modified to ensure that code isn't DCE'd).

<details>
  <summary>Table</summary>

tests | before/master | with optimization / this PR | percentage
reduction
-- | -- | -- | --
Testing should_pass/evm/evm_basic | 28 | 28 | 0
Testing should_pass/payable_non_zero_coins | 196 | 196 | 0
Testing should_pass/dca/log_intrinsic | 52 | 52 | 0
Testing should_pass/dca/constant_struct | 56 | 56 | 0
Testing should_pass/dca/unused_enum | 44 | 28 | 36.3636363636364
Testing should_pass/dca/unused_variable | 28 | 28 | 0
Testing should_pass/dca/unused_free_fn | 28 | 28 | 0
Testing should_pass/dca/log_stdlib | 52 | 52 | 0
Testing should_pass/dca/multiple_fns_same_name | 28 | 28 | 0
Testing should_pass/dca/alias_type_ascription_generic | 28 | 28 | 0
Testing should_pass/dca/trait_method | 28 | 28 | 0
Testing should_pass/dca/generic_fn_trait_contraint | 28 | 28 | 0
Testing should_pass/dca/impl_self_alias | 28 | 28 | 0
Testing should_pass/dca/unused_variable_in_free_fn | 28 | 28 | 0
Testing should_pass/dca/allow_dead_code | 68 | 28 | 58.8235294117647
Testing should_pass/dca/contract/unused_struct_field_array | 100 | 100 |
0
Testing should_pass/dca/contract/unused_struct_field | 68 | 68 | 0
Testing should_pass/dca/contract/unused_struct_field_enum | 92 | 92 | 0
Testing should_pass/dca/contract/unused_struct_field_tuple | 68 | 68 | 0
Testing should_pass/dca/contract/abi_fn_params | 100 | 100 | 0
Testing should_pass/dca/impl_self_alias2 | 28 | 28 | 0
Testing should_pass/dca/multiple_enums_same_name | 52 | 28 |
46.1538461538462
Testing should_pass/dca/trait_method_lib | 28 | 28 | 0
Testing should_pass/dca/func_param | 60 | 28 | 53.3333333333333
Testing should_pass/dca/unused_fields | 52 | 28 | 46.1538461538462
Testing should_pass/dca/impl_self | 28 | 28 | 0
Testing should_pass/dca/constant_decl_expr | 44 | 44 | 0
Testing should_pass/dca/impl_trait_single | 52 | 28 | 46.1538461538462
Testing should_pass/dca/unused_trait | 28 | 28 | 0
Testing should_pass/dca/impl_unused_fn | 60 | 60 | 0
Testing should_pass/dca/all_paths_return | 28 | 28 | 0
Testing should_pass/dca/unused_struct | 28 | 28 | 0
Testing should_pass/dca/alias_unused | 28 | 28 | 0
Testing should_pass/dca/trait_method_neq | 116 | 116 | 0
Testing should_pass/dca/struct_field_no_warning | 36 | 36 | 0
Testing should_pass/dca/alias_type_ascription | 28 | 28 | 0
Testing should_pass/dca/impl_trait_multiple | 76 | 28 | 63.1578947368421
Testing should_pass/dca/alias_lib | 108 | 84 | 22.2222222222222
Testing should_pass/dca/constant_while | 28 | 28 | 0
Testing should_pass/dca/library/unused_priv_free_fn | 28 | 28 | 0
Testing should_pass/dca/library/fn_params_impl | 28 | 28 | 0
Testing should_pass/dca/library/unused_pub_free_fn | 28 | 28 | 0
Testing should_pass/dca/library/fn_params_trait | 28 | 28 | 0
Testing should_pass/dca/library/fn_params_free | 28 | 28 | 0
Testing should_pass/supertraits_via_self | 36 | 36 | 0
Testing should_pass/midenvm/midenvm_trivial ...Testing
should_pass/static_analysis/cei_pattern_violation_storage_var_update |
460 | 460 | 0
Testing should_pass/static_analysis/cei_pattern_violation_in_struct |
380 | 340 | 10.5263157894737
Testing should_pass/static_analysis/cei_pattern_violation | 340 | 340 |
0
Testing
should_pass/static_analysis/cei_pattern_violation_in_asm_block_bal | 212
| 212 | 0
Testing should_pass/static_analysis/cei_pattern_violation_in_tuple | 380
| 340 | 10.5263157894737
Testing
should_pass/static_analysis/cei_pattern_violation_in_if_statement-2 |
340 | 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_asm_block_tr | 332
| 332 | 0
Testing
should_pass/static_analysis/storage_annotations_unused_read_and_write |
60 | 60 | 0
Testing should_pass/static_analysis/cei_pattern_violation_smo_intrinsic
| 704 | 720 | -2.27272727272727
Testing
should_pass/static_analysis/cei_pattern_violation_in_while_loop-2 | 340
| 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_while_loop-3 | 340
| 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_storage_struct_read |
548 | 524 | 4.37956204379562
Testing
should_pass/static_analysis/cei_pattern_violation_in_asm_block_read |
224 | 224 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_intrinsic_call |
340 | 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_storage_map_and_vec |
2224 | 2060 | 7.37410071942446
Testing should_pass/static_analysis/cei_pattern_violation_in_func_app-3
| 340 | 340 | 0
Testing should_pass/static_analysis/cei_pattern_violation_in_func_app-1
| 340 | 340 | 0
Testing should_pass/static_analysis/cei_pattern_violation_in_func_app-2
| 340 | 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_asm_block_smo | 220
| 220 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_while_loop-4 | 340
| 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_storage_var_read | 532
| 524 | 1.50375939849624
Testing should_pass/static_analysis/storage_annotations_unused_read | 60
| 60 | 0
Testing should_pass/static_analysis/cei_pattern_violation_in_asm_block |
240 | 240 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_codeblocks_other_than_in_functions
| 340 | 340 | 0
Testing should_pass/static_analysis/storage_annotations_unused_write |
60 | 60 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_asm_block_tro |
1332 | 1340 | -0.600600600600601
Testing
should_pass/static_analysis/cei_pattern_violation_in_while_loop-1 | 340
| 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_standalone_function
| 340 | 340 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_if_statement-1 |
364 | 364 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_in_match_statement-1 |
588 | 588 | 0
Testing
should_pass/static_analysis/cei_pattern_violation_more_complex_logic |
8196 | 7880 | 3.8555392874573
Testing should_pass/non_payable_zero_coins_let_binding | 240 | 240 | 0
Testing should_pass/forc/contract_dependencies/contract_a | 188 | 60 |
68.0851063829787
Testing should_pass/forc/contract_dependencies/contract_b | 60 | 60 | 0
Testing should_pass/forc/contract_dependencies/contract_c | 60 | 60 | 0
Testing should_pass/forc/parent_pkg_manifest/contract_a | 60 | 60 | 0
Testing should_pass/forc/dependency_package_field | 28 | 28 | 0
Testing should_pass/forc/workspace_building | 28 | 28 | 0
Testing should_pass/forc/dependency_patching | 28 | 28 | 0
Testing should_pass/supertraits_for_abis | 84 | 84 | 0
Testing should_pass/non_payable_implicit_zero_coins | 240 | 240 | 0
Testing should_pass/language/smo | 1052 | 1052 | 0
Testing should_pass/language/generic_impl_self | 2348 | 2308 |
1.70357751277683
Testing should_pass/language/configurable_consts | 4084 | 4084 | 0
Testing should_pass/language/const_decl_in_library | 68 | 68 | 0
Testing should_pass/language/tuple_single_element | 124 | 108 |
12.9032258064516
Testing should_pass/language/gtf_intrinsic | 60 | 92 | -53.3333333333333
Testing should_pass/language/is_prime | 844 | 844 | 0
Testing should_pass/language/asm_without_return | 52 | 52 | 0
Testing should_pass/language/const_decl_and_use_in_library | 60 | 60 | 0
Testing should_pass/language/generic_result_method | 588 | 580 |
1.36054421768707
Testing should_pass/language/mutable_arrays_swap | 220 | 116 |
47.2727272727273
Testing should_pass/language/insert_element_reg_reuse | 5380 | 5108 |
5.05576208178439
Testing should_pass/language/mutable_arrays_enum | 276 | 156 |
43.4782608695652
Testing should_pass/language/contract_caller_as_ret | 60 | 60 | 0
Testing should_pass/language/left_to_right_func_args_evaluation | 188 |
188 | 0
Testing should_pass/language/builtin_type_method_call | 44 | 44 | 0
Testing should_pass/language/binary_and_hex_literals | 28 | 28 | 0
Testing should_pass/language/predicate_while_dep | 60 | 60 | 0
Testing should_pass/language/generic_enum | 132 | 28 | 78.7878787878788
Testing should_pass/language/method_on_empty_struct | 28 | 28 | 0
Testing should_pass/language/prelude_access2 | 28 | 28 | 0
Testing should_pass/language/ret_small_string | 56 | 56 | 0
Testing should_pass/language/eq_and_neq | 1532 | 1172 | 23.4986945169713
Testing should_pass/language/match_expressions_with_self | 364 | 364 | 0
Testing should_pass/language/nested_while_and_if | 156 | 156 | 0
Testing should_pass/language/multi_item_import | 100 | 28 | 72
Testing should_pass/language/binop_intrinsics | 1484 | 1484 | 0
Testing should_pass/language/mutable_arrays_multiple_nested | 172 | 172
| 0
Testing should_pass/language/far_jumps/many_blobs | 1064120 | 1064120 |
0
Testing should_pass/language/far_jumps/single_blob | 1048648 | 1048648 |
0
Testing should_pass/language/nested_struct_destructuring | 472 | 52 |
88.9830508474576
Testing should_pass/language/associated_const_abi_multiple | 28 | 28 | 0
Testing should_pass/language/struct_field_access | 116 | 92 |
20.6896551724138
Testing should_pass/language/out_of_order_decl | 132 | 52 |
60.6060606060606
Testing should_pass/language/diagnose_unknown_annotations | 84 | 84 | 0
Testing should_pass/language/associated_const_trait | 28 | 28 | 0
Testing should_pass/language/typeinfo_custom_callpath2 | 100 | 100 | 0
Testing should_pass/language/mutable_arrays | 116 | 116 | 0
Testing should_pass/language/prelude_access | 28 | 28 | 0
Testing should_pass/language/match_expressions_empty_enums | 44 | 44 | 0
Testing should_pass/language/implicit_casting | 84 | 44 |
47.6190476190476
Testing should_pass/language/generic_type_inference | 4116 | 3972 |
3.49854227405248
Testing should_pass/language/tuple_in_struct | 804 | 804 | 0
Testing should_pass/language/associated_const_impl_local_same_name | 28
| 28 | 0
Testing should_pass/language/where_clause_functions | 1364 | 1332 |
2.34604105571848
Testing should_pass/language/revert_in_first_if_branch | 44 | 44 | 0
Testing should_pass/language/mutable_arrays_nested | 196 | 196 | 0
Testing should_pass/language/if_elseif_enum | 1324 | 756 |
42.9003021148036
Testing should_pass/language/logging | 472 | 472 | 0
Testing should_pass/language/enum_if_let_large_type | 436 | 436 | 0
Testing should_pass/language/type_alias | 6048 | 5552 | 8.2010582010582
Testing should_pass/language/import_method_from_other_file | 452 | 452 |
0
Testing should_pass/language/enum_in_fn_decl | 116 | 116 | 0
Testing should_pass/language/where_clause_impls | 572 | 572 | 0
Testing should_pass/language/generic_impl_self_where | 2580 | 2068 |
19.8449612403101
Testing should_pass/language/shadowing/shadowed_glob_imports | 28 | 28 |
0
Testing should_pass/language/tuple_field_reassignment | 1188 | 1188 | 0
Testing should_pass/language/enum_if_let | 600 | 600 | 0
Testing should_pass/language/match_expressions_enums | 156 | 148 |
5.12820512820513
Testing should_pass/language/ref_mutable_arrays | 116 | 116 | 0
Testing should_pass/language/array_basics | 1428 | 964 |
32.4929971988796
Testing should_pass/language/associated_const_abi | 28 | 28 | 0
Testing should_pass/language/mutable_arrays_struct | 156 | 156 | 0
Testing should_pass/language/numeric_constants | 252 | 252 | 0
Testing should_pass/language/use_full_path_names | 76 | 28 |
63.1578947368421
Testing should_pass/language/match_expressions_simple | 340 | 340 | 0
Testing should_pass/language/associated_const_impl_self | 28 | 28 | 0
Testing should_pass/language/enum_destructuring | 124 | 124 | 0
Testing should_pass/language/zero_field_types | 44 | 44 | 0
Testing should_pass/language/typeinfo_custom_callpath_with_import | 164
| 164 | 0
Testing should_pass/language/generic_structs | 140 | 52 |
62.8571428571429
Testing should_pass/language/where_clause_methods | 1364 | 1332 |
2.34604105571848
Testing should_pass/language/valid_impurity | 60 | 60 | 0
Testing should_pass/language/abort_control_flow_good | 44 | 44 | 0
Testing should_pass/language/const_inits | 2644 | 2668 |
-0.907715582450832
Testing should_pass/language/tuple_indexing | 68 | 68 | 0
Testing should_pass/language/match_expressions_rest | 236 | 156 |
33.8983050847458
Testing should_pass/language/ref_mutable_fn_args_u32 | 60 | 60 | 0
Testing should_pass/language/method_type_args | 28 | 28 | 0
Testing should_pass/language/array_generics | 404 | 84 |
79.2079207920792
Testing should_pass/language/tuple_types | 84 | 44 | 47.6190476190476
Testing should_pass/language/reassignment_operators | 356 | 356 | 0
Testing should_pass/language/match_expressions_constants | 84 | 84 | 0
Testing should_pass/language/match_expressions_inside_generic_functions
| 428 | 428 | 0
Testing should_pass/language/if_implicit_unit | 28 | 28 | 0
Testing should_pass/language/enum_variant_imports | 716 | 716 | 0
Testing should_pass/language/where_clause_structs | 244 | 244 | 0
Testing should_pass/language/eq_intrinsic | 412 | 412 | 0
Testing should_pass/language/match_expressions_nested | 172 | 164 |
4.65116279069768
Testing should_pass/language/match_expressions_explicit_rets | 28 | 28 |
0
Testing should_pass/language/while_loops | 364 | 364 | 0
Testing should_pass/language/supertraits_with_trait_methods | 148 | 148
| 0
Testing should_pass/language/const_decl_with_call_path | 340 | 324 |
4.70588235294118
Testing should_pass/language/break_and_continue_block_ret | 28 | 28 | 0
Testing should_pass/language/main_args/main_args_ref_ref | 52 | 52 | 0
Testing should_pass/language/main_args/main_args_ref_copy | 52 | 52 | 0
Testing should_pass/language/main_args/main_args_copy | 44 | 44 | 0
Testing should_pass/language/main_args/main_args_empty | 44 | 44 | 0
Testing should_pass/language/main_args/main_args_ref | 44 | 44 | 0
Testing should_pass/language/main_args/main_args_various_types | 452 |
452 | 0
Testing should_pass/language/main_args/main_args_copy_copy | 44 | 44 | 0
Testing should_pass/language/smo_opcode | 112 | 112 | 0
Testing should_pass/language/match_expressions_structs | 220 | 212 |
3.63636363636364
Testing should_pass/language/same_const_name | 732 | 732 | 0
Testing should_pass/language/retd_b256 | 120 | 120 | 0
Testing should_pass/language/bool_and_or | 76 | 76 | 0
Testing should_pass/language/local_impl_for_ord | 52 | 28 |
46.1538461538462
Testing should_pass/language/import_trailing_comma | 84 | 84 | 0
Testing should_pass/language/modulo_uint_test | 420 | 420 | 0
Testing should_pass/language/struct_field_reassignment | 160 | 28 | 82.5
Testing should_pass/language/complex_cfg | 420 | 340 | 19.047619047619
Testing should_pass/language/nested_structs | 1740 | 1700 |
2.29885057471264
Testing should_pass/language/generics_in_contract | 680 | 584 |
14.1176470588235
Testing should_pass/language/match_expressions_or | 660 | 660 | 0
Testing should_pass/language/match_expressions | 44 | 44 | 0
Testing should_pass/language/same_const_name_lib | 28 | 28 | 0
Testing should_pass/language/associated_const_trait_default | 44 | 44 |
0
Testing should_pass/language/retd_struct | 120 | 120 | 0
Testing should_pass/language/b256_bitwise_ops | 30144 | 30128 |
0.0530785562632696
Testing should_pass/language/ref_mutable_fn_args_call | 60 | 60 | 0
Testing should_pass/language/generic_transpose | 852 | 828 |
2.8169014084507
Testing should_pass/language/struct_destructuring | 220 | 188 |
14.5454545454545
Testing should_pass/language/trait_import_with_star | 52 | 52 | 0
Testing should_pass/language/arg_demotion_inline | 164 | 60 |
63.4146341463415
Testing should_pass/language/typeinfo_custom_callpath | 100 | 100 | 0
Testing should_pass/language/diverging_exprs | 1604 | 1572 |
1.99501246882793
Testing should_pass/language/empty_method_initializer | 440 | 416 |
5.45454545454545
Testing should_pass/language/raw_identifiers | 140 | 140 | 0
Testing should_pass/language/generic_struct | 68 | 44 | 35.2941176470588
Testing should_pass/language/doc_comments | 132 | 76 | 42.4242424242424
Testing should_pass/language/basic_func_decl | 108 | 28 |
74.0740740740741
Testing should_pass/language/retd_small_array | 60 | 60 | 0
Testing should_pass/language/b256_bad_jumps | 28 | 28 | 0
Testing should_pass/language/ref_mutable_fn_args_bool | 52 | 52 | 0
Testing should_pass/language/chained_if_let | 212 | 204 |
3.77358490566038
Testing should_pass/language/self_impl_reassignment | 1140 | 1132 |
0.701754385964912
Testing should_pass/language/generic_inside_generic | 196 | 196 | 0
Testing should_pass/language/test_multiple_attributes | 28 | 28 | 0
Testing should_pass/language/ref_mutable_fn_args_struct_assign | 92 | 92
| 0
Testing should_pass/language/tuple_trait | 164 | 140 | 14.6341463414634
Testing should_pass/language/redundant_return | 28 | 28 | 0
Testing should_pass/language/predicate_while | 60 | 60 | 0
Testing should_pass/language/basic_predicate | 28 | 28 | 0
Testing should_pass/language/ops | 7452 | 7452 | 0
Testing should_pass/language/multi_impl_self | 44 | 44 | 0
Testing should_pass/language/new_allocator_test | 684 | 684 | 0
Testing should_pass/language/mutable_and_initd | 156 | 156 | 0
Testing should_pass/language/unit_type_variants | 60 | 60 | 0
Testing should_pass/language/tuple_desugaring | 212 | 228 |
-7.54716981132076
Testing should_pass/language/unary_not_basic | 28 | 28 | 0
Testing should_pass/language/main_returns_unit | 28 | 28 | 0
Testing should_pass/language/match_expressions_mismatched | 180 | 116 |
35.5555555555556
Testing should_pass/language/asm_expr_basic | 252 | 228 |
9.52380952380952
Testing should_pass/language/many_stack_variables | 484 | 484 | 0
Testing should_pass/language/size_of | 324 | 180 | 44.4444444444444
Testing should_pass/language/test_attribute | 28 | 28 | 0
Testing should_pass/language/bitwise_not | 308 | 308 | 0
Testing should_pass/language/primitive_type_argument | 168 | 44 |
73.8095238095238
Testing should_pass/language/associated_const_impl | 28 | 28 | 0
Testing should_pass/language/associated_const_trait_impl_method | 28 |
28 | 0
Testing should_pass/language/generic_functions | 28 | 28 | 0
Testing should_pass/language/is_reference_type | 532 | 340 |
36.0902255639098
Testing should_pass/language/integer_type_inference | 28 | 28 | 0
Testing should_pass/language/import_with_different_callpaths | 2588 |
2460 | 4.94590417310665
Testing should_pass/language/funcs_with_generic_types | 28 | 28 | 0
Testing should_pass/language/enum_init_fn_call | 740 | 740 | 0
Testing should_pass/language/tuple_access | 296 | 144 | 51.3513513513514
Testing should_pass/language/impure_ifs | 692 | 692 | 0
Testing should_pass/language/associated_const_impl_multiple | 28 | 28 |
0
Testing should_pass/language/enum_type_inference | 152 | 44 |
71.0526315789474
Testing should_pass/language/inline_if_expr_const | 28 | 28 | 0
Testing should_pass/language/attributes_warnings | 28 | 28 | 0
Testing should_pass/language/contract_caller_dynamic_address | 144 | 144
| 0
Testing should_pass/language/ret_string_in_struct | 80 | 80 | 0
Testing should_pass/language/associated_const_trait_method | 28 | 28 | 0
Testing should_pass/language/where_clause_enums | 396 | 372 |
6.06060606060606
Testing should_pass/language/non_literal_const_decl | 44 | 44 | 0
Testing should_pass/language/supertraits | 3708 | 3708 | 0
Testing should_pass/language/struct_init_reorder | 116 | 116 | 0
Testing should_pass/language/generic_traits | 1092 | 1020 |
6.59340659340659
Testing should_pass/language/where_clause_traits | 44 | 28 |
36.3636363636364
Testing should_pass/language/implicit_return | 44 | 44 | 0
Testing should_pass/language/raw_ptr/raw_ptr_ret | 364 | 356 |
2.1978021978022
Testing should_pass/language/raw_ptr/vec_ret | 828 | 828 | 0
Testing should_pass/language/break_and_continue | 700 | 700 | 0
Testing should_pass/language/aliased_imports | 60 | 60 | 0
Testing should_pass/language/enum_padding | 116 | 116 | 0
Testing should_pass/language/fix_opcode_bug | 148 | 148 | 0
Testing should_pass/language/unary_not_basic_2 | 28 | 28 | 0
Testing should_pass/language/associated_const_trait_const | 28 | 28 | 0
Testing should_pass/language/associated_const_abi_default | 28 | 28 | 0
Testing should_pass/language/ref_mutable_arrays_inline | 116 | 116 | 0
Testing should_pass/language/b256_ops | 1072 | 1064 | 0.746268656716418
Testing should_pass/language/retd_zero_len_array | 36 | 36 | 0
Testing should_pass/language/ref_mutable_fn_args_struct | 84 | 84 | 0
Testing should_pass/language/op_precedence | 28 | 28 | 0
Testing should_pass/language/const_decl | 44 | 44 | 0
Testing should_pass/unit_tests/nested_libs | 212 | 212 | 0
Testing should_pass/unit_tests/script_multi_test | 128 | 128 | 0
Testing should_pass/unit_tests/predicate_multi_test | 188 | 188 | 0
Testing should_pass/unit_tests/contract_multi_test | 396 | 396 | 0
Testing should_pass/unit_tests/script_with_nested_libs | 220 | 220 | 0
Testing should_pass/unit_tests/lib_single_test | 100 | 100 | 0
Testing should_pass/unit_tests/contract-multi-contract-calls | 60 | 60 |
0
Testing should_pass/unit_tests/workspace_test | 128 | 128 | 0
Testing should_pass/unit_tests/script-contract-calls | 60 | 60 | 0
Testing should_pass/unit_tests/stack_indexing_overflow | 4196 | 4116 |
1.90657769304099
Testing should_pass/unit_tests/contract_with_nested_libs | 252 | 252 | 0
Testing should_pass/unit_tests/should_revert | 52 | 52 | 0
Testing should_pass/unit_tests/lib_multi_test | 236 | 236 | 0
Testing should_pass/unit_tests/predicate_with_nested_libs | 180 | 180 |
0
Testing should_pass/test_contracts/context_testing_contract | 364 | 364
| 0
Testing should_pass/test_contracts/increment_contract | 844 | 844 | 0
Testing should_pass/test_contracts/storage_configurable | 60 | 60 | 0
Testing should_pass/test_contracts/issue_1512_repro | 228 | 228 | 0
Testing should_pass/test_contracts/abi_with_same_name_types | 104 | 104
| 0
Testing should_pass/test_contracts/abi_with_tuples_contract | 92 | 92 |
0
Testing should_pass/test_contracts/array_of_structs_contract | 188 | 188
| 0
Testing should_pass/test_contracts/storage_access_contract | 11084 |
11084 | 0
Testing should_pass/test_contracts/test_fuel_coin_contract | 172 | 172 |
0
Testing should_pass/test_contracts/balance_test_contract | 76 | 76 | 0
Testing should_pass/test_contracts/nested_struct_args_contract | 92 | 92
| 0
Testing should_pass/test_contracts/multiple_impl | 84 | 84 | 0
Testing should_pass/test_contracts/contract_with_type_aliases | 172 |
172 | 0
Testing should_pass/test_contracts/basic_storage | 12708 | 12476 |
1.82562165564998
Testing should_pass/test_contracts/return_struct | 388 | 304 |
21.6494845360825
Testing should_pass/test_contracts/auth_testing_contract | 68 | 68 | 0
Testing should_pass/test_contracts/abi_with_generic_types | 168 | 168 |
0
Testing should_pass/supertraits_for_abis_diamond | 60 | 60 | 0
Testing should_pass/multiple_supertraits_for_abis | 84 | 84 | 0
Testing should_pass/conditional_compilation/run | 44 | 44 | 0
Testing should_pass/conditional_compilation/compile ...Testing
should_pass/supertraits_for_abis_ownable | 2044 | 2020 |
1.17416829745597
Testing should_pass/empty_fields_in_storage_struct | 11176 | 10688 |
4.36649964209019
Testing should_pass/stdlib/eq_generic | 28 | 28 | 0
Testing should_pass/stdlib/if_type_revert | 28 | 28 | 0
Testing should_pass/stdlib/exponentiation_test | 2436 | 2436 | 0
Testing should_pass/stdlib/u256_test | 5444 | 4540 | 16.6054371785452
Testing should_pass/stdlib/b512_struct_alignment | 360 | 328 |
8.88888888888889
Testing should_pass/stdlib/u128_root_test | 38716 | 33356 |
13.8444054137824
Testing should_pass/stdlib/u128_div_test | 5092 | 4236 |
16.8106834249804
Testing should_pass/stdlib/ge_test | 316 | 316 | 0
Testing should_pass/stdlib/eq_custom_type | 804 | 772 | 3.98009950248756
Testing should_pass/stdlib/identity_eq | 6540 | 6484 | 0.856269113149847
Testing should_pass/stdlib/u128_test | 7644 | 6804 | 10.989010989011
Testing should_pass/stdlib/require | 176 | 92 | 47.7272727272727
Testing should_pass/stdlib/u256_mul_test | 44612 | 33508 |
24.8901640814131
Testing should_pass/stdlib/b512_test | 3488 | 2704 | 22.4770642201835
Testing should_pass/stdlib/block_height | 68 | 68 | 0
Testing should_pass/stdlib/assert_test | 212 | 212 | 0
Testing should_pass/stdlib/assert_eq | 3252 | 2764 | 15.0061500615006
Testing should_pass/stdlib/intrinsics | 600 | 388 | 35.3333333333333
Testing should_pass/stdlib/storage_vec_insert | 2352 | 2320 |
1.36054421768707
Testing should_pass/stdlib/option | 22680 | 21144 | 6.77248677248677
Testing should_pass/stdlib/u128_log_test | 38492 | 32764 |
14.8810142367245
Testing should_pass/stdlib/chess | 2084 | 2084 | 0
Testing should_pass/stdlib/contract_id_test | 296 | 264 |
10.8108108108108
Testing should_pass/stdlib/assert_eq_revert | 60 | 60 | 0
Testing should_pass/stdlib/vec_swap | 29332 | 26940 | 8.15491613255148
Testing should_pass/stdlib/u256_div_test | 50796 | 37892 |
25.4035750846523
Testing should_pass/stdlib/u128_mul_test | 1740 | 1356 |
22.0689655172414
Testing should_pass/stdlib/address_test | 2344 | 1928 | 17.7474402730375
Testing should_pass/stdlib/raw_ptr | 1732 | 1732 | 0
Testing should_pass/stdlib/u256_ops_test | 42980 | 31156 |
27.51046998604
Testing should_pass/stdlib/result | 7176 | 6200 | 13.6008918617614
Testing should_pass/stdlib/raw_slice | 596 | 532 | 10.738255033557
Testing should_pass/stdlib/vec | 152352 | 139208 | 8.62738920394875
Testing should_pass/stdlib/contract_id_type | 380 | 340 |
10.5263157894737
Testing should_pass/stdlib/logarithmic_test | 1348 | 1348 | 0
</details>

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
vaivaswatha authored May 31, 2023
1 parent 999410c commit 84e3855
Show file tree
Hide file tree
Showing 16 changed files with 954 additions and 87 deletions.
2 changes: 2 additions & 0 deletions sway-ir/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ pub mod call_graph;
pub use call_graph::*;
pub mod dominator;
pub use dominator::*;
pub mod escaped_symbols;
pub use escaped_symbols::*;
133 changes: 133 additions & 0 deletions sway-ir/src/analysis/escaped_symbols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! An analysis to compute symbols that escape out from a function.
//! This could be into another function, or via ptr_to_int etc.
//! Any transformations involving such symbols are unsafe.
use rustc_hash::FxHashSet;

use crate::{
AnalysisResult, AnalysisResultT, AnalysisResults, BlockArgument, Context, Function,
Instruction, IrError, LocalVar, Pass, PassMutability, ScopedPass, Type, Value, ValueDatum,
};

pub const ESCAPED_SYMBOLS_NAME: &str = "escaped_symbols";

pub fn create_escaped_symbols_pass() -> Pass {
Pass {
name: ESCAPED_SYMBOLS_NAME,
descr: "Symbols that escape / cannot be analysed",
deps: vec![],
runner: ScopedPass::FunctionPass(PassMutability::Analysis(compute_escaped_symbols_pass)),
}
}

#[derive(Eq, PartialEq, Copy, Clone, Hash)]
pub enum Symbol {
Local(LocalVar),
Arg(BlockArgument),
}

impl Symbol {
pub fn get_type(&self, context: &Context) -> Type {
match self {
Symbol::Local(l) => l.get_type(context),
Symbol::Arg(ba) => ba.ty,
}
}

pub fn _get_name(&self, context: &Context, function: Function) -> String {
match self {
Symbol::Local(l) => function.lookup_local_name(context, l).unwrap().clone(),
Symbol::Arg(ba) => format!("{}[{}]", ba.block.get_label(context), ba.idx),
}
}
}

// A value may (indirectly) refer to one or more symbols.
pub fn get_symbols(context: &Context, val: Value) -> Vec<Symbol> {
let mut visited = FxHashSet::default();
fn get_symbols_rec(
context: &Context,
visited: &mut FxHashSet<Value>,
val: Value,
) -> Vec<Symbol> {
if visited.contains(&val) {
return vec![];
}
visited.insert(val);
match context.values[val.0].value {
ValueDatum::Instruction(Instruction::GetLocal(local)) => vec![Symbol::Local(local)],
ValueDatum::Instruction(Instruction::GetElemPtr { base, .. }) => {
get_symbols_rec(context, visited, base)
}
ValueDatum::Argument(b) => {
if b.block.get_label(context) == "entry" {
vec![Symbol::Arg(b)]
} else {
b.block
.pred_iter(context)
.map(|pred| b.get_val_coming_from(context, pred).unwrap())
.flat_map(|v| get_symbols_rec(context, visited, v))
.collect()
}
}
_ => vec![],
}
}
get_symbols_rec(context, &mut visited, val)
}

pub fn get_symbol(context: &Context, val: Value) -> Option<Symbol> {
let syms = get_symbols(context, val);
(syms.len() == 1).then(|| syms[0])
}

pub type EscapedSymbols = FxHashSet<Symbol>;
impl AnalysisResultT for EscapedSymbols {}

pub fn compute_escaped_symbols_pass(
context: &Context,
_: &AnalysisResults,
function: Function,
) -> Result<AnalysisResult, IrError> {
Ok(Box::new(compute_escaped_symbols(context, &function)))
}

pub fn compute_escaped_symbols(context: &Context, function: &Function) -> EscapedSymbols {
let mut result = FxHashSet::default();

let add_from_val = |result: &mut FxHashSet<Symbol>, val: &Value| {
get_symbols(context, *val).iter().for_each(|s| {
result.insert(*s);
});
};

for (_block, inst) in function.instruction_iter(context) {
match inst.get_instruction(context).unwrap() {
Instruction::AsmBlock(_, args) => {
for arg_init in args.iter().filter_map(|arg| arg.initializer) {
add_from_val(&mut result, &arg_init)
}
}
Instruction::BinaryOp { .. } => (),
Instruction::BitCast(_, _) => (),
Instruction::Branch(_) => (),
Instruction::Call(_, args) => args.iter().for_each(|v| add_from_val(&mut result, v)),
Instruction::CastPtr(_, _) => (),
Instruction::Cmp(_, _, _) => (),
Instruction::ConditionalBranch { .. } => (),
Instruction::ContractCall { params, .. } => add_from_val(&mut result, params),
Instruction::FuelVm(_) => (),
Instruction::GetLocal(_) => (),
Instruction::GetElemPtr { .. } => (),
Instruction::IntToPtr(_, _) => (),
Instruction::Load(_) => (),
Instruction::MemCopyBytes { .. } => (),
Instruction::MemCopyVal { .. } => (),
Instruction::Nop => (),
Instruction::PtrToInt(v, _) => add_from_val(&mut result, v),
Instruction::Ret(_, _) => (),
Instruction::Store { .. } => (),
}
}

result
}
12 changes: 7 additions & 5 deletions sway-ir/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ pub struct BlockArgument {
impl BlockArgument {
/// Get the actual parameter passed to this block argument from `from_block`
pub fn get_val_coming_from(&self, context: &Context, from_block: &Block) -> Option<Value> {
for pred in self.block.pred_iter(context) {
for BranchToWithArgs { block, args } in pred.successors(context) {
if block == *from_block {
return Some(args[self.idx]);
}
for BranchToWithArgs {
block: succ_block,
args,
} in from_block.successors(context)
{
if succ_block == self.block {
return Some(args[self.idx]);
}
}
None
Expand Down
8 changes: 4 additions & 4 deletions sway-ir/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,11 @@ impl Instruction {
| Instruction::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
| Instruction::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
| Instruction::FuelVm(FuelVmInstruction::StateStoreWord { .. })
| Instruction::FuelVm(FuelVmInstruction::Revert(..))
| Instruction::MemCopyBytes { .. }
| Instruction::MemCopyVal { .. }
| Instruction::Store { .. } => true,
| Instruction::Store { .. }
| Instruction::Ret(..) => true,

Instruction::BinaryOp { .. }
| Instruction::BitCast(..)
Expand All @@ -546,15 +548,13 @@ impl Instruction {
| Instruction::ConditionalBranch { .. }
| Instruction::FuelVm(FuelVmInstruction::Gtf { .. })
| Instruction::FuelVm(FuelVmInstruction::ReadRegister(_))
| Instruction::FuelVm(FuelVmInstruction::Revert(..))
| Instruction::FuelVm(FuelVmInstruction::StateLoadWord(_))
| Instruction::GetElemPtr { .. }
| Instruction::GetLocal(_)
| Instruction::IntToPtr(..)
| Instruction::Load(_)
| Instruction::Nop
| Instruction::PtrToInt(..)
| Instruction::Ret(..) => false,
| Instruction::PtrToInt(..) => false,
}
}

Expand Down
83 changes: 81 additions & 2 deletions sway-ir/src/irtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! [`Aggregate`] is an abstract collection of [`Type`]s used for structs, unions and arrays,
//! though see below for future improvements around splitting arrays into a different construct.
use crate::{context::Context, pretty::DebugWithContext};
use crate::{context::Context, pretty::DebugWithContext, Constant, ConstantValue, Value};

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)]
pub struct Type(pub generational_arena::Index);
Expand Down Expand Up @@ -281,7 +281,7 @@ impl Type {
}
}

/// What's the type of the struct value indexed by indices.
/// What's the type of the struct/array value indexed by indices.
pub fn get_indexed_type(&self, context: &Context, indices: &[u64]) -> Option<Type> {
if indices.is_empty() {
return None;
Expand All @@ -295,6 +295,49 @@ impl Type {
})
}

/// What's the offset of the indexed element?
/// It may not always be possible to determine statically.
pub fn get_indexed_offset(&self, context: &Context, indices: &[Value]) -> Option<u64> {
indices
.iter()
.fold(Some((*self, 0)), |ty, idx| {
let Some(Constant {
value: ConstantValue::Uint(idx),
ty: _,
}) = idx.get_constant(context) else { return None; };
ty.and_then(|(ty, accum_offset)| {
if ty.is_struct(context) {
// Sum up all sizes of all previous fields.
let prev_idxs_offset = (0..(*idx)).try_fold(0, |accum, pre_idx| {
ty.get_field_type(context, pre_idx)
.map(|field_ty| field_ty.size_in_bytes(context) + accum)
})?;
ty.get_field_type(context, *idx)
.map(|field_ty| (field_ty, accum_offset + prev_idxs_offset))
} else if ty.is_union(context) {
ty.get_field_type(context, *idx)
.map(|field_ty| (field_ty, accum_offset))
} else {
assert!(
ty.is_array(context),
"Expected aggregate type when indexing using GEP. Got {}",
ty.as_string(context)
);
// size_of_element * idx will be the offset of idx.
ty.get_array_elem_type(context).map(|elm_ty| {
let prev_idxs_offset = ty
.get_array_elem_type(context)
.unwrap()
.size_in_bytes(context)
* idx;
(elm_ty, accum_offset + prev_idxs_offset)
})
}
})
})
.map(|pair| pair.1)
}

pub fn get_field_type(&self, context: &Context, idx: u64) -> Option<Type> {
if let TypeContent::Struct(fields) | TypeContent::Union(fields) = self.get_content(context)
{
Expand Down Expand Up @@ -339,6 +382,42 @@ impl Type {
_ => vec![],
}
}

pub fn size_in_bytes(&self, context: &Context) -> u64 {
match self.get_content(context) {
TypeContent::Unit
| TypeContent::Bool
| TypeContent::Uint(_)
| TypeContent::Pointer(_) => 8,
TypeContent::Slice => 16,
TypeContent::B256 => 32,
TypeContent::String(n) => super::size_bytes_round_up_to_word_alignment!(*n),
TypeContent::Array(el_ty, cnt) => cnt * el_ty.size_in_bytes(context),
TypeContent::Struct(field_tys) => {
// Sum up all the field sizes.
field_tys
.iter()
.map(|field_ty| field_ty.size_in_bytes(context))
.sum()
}
TypeContent::Union(field_tys) => {
// Find the max size for field sizes.
field_tys
.iter()
.map(|field_ty| field_ty.size_in_bytes(context))
.max()
.unwrap_or(0)
}
}
}
}

// This is a mouthful...
#[macro_export]
macro_rules! size_bytes_round_up_to_word_alignment {
($bytes_expr: expr) => {
($bytes_expr + 7) - (($bytes_expr + 7) % 8)
};
}

/// A helper to check if an Option<Type> value is of a particular Type.
Expand Down
Loading

0 comments on commit 84e3855

Please sign in to comment.