solstat/analyzer/optimizations/
pack_storage_variables.rs

1use std::collections::HashSet;
2
3use solang_parser::pt::{self, Loc};
4use solang_parser::{self, pt::SourceUnit};
5
6use crate::analyzer::ast::{self, Target};
7use crate::analyzer::utils;
8
9pub fn pack_storage_variables_optimization(source_unit: SourceUnit) -> HashSet<Loc> {
10    let mut optimization_locations: HashSet<Loc> = HashSet::new();
11
12    let target_nodes =
13        ast::extract_target_from_node(Target::ContractDefinition, source_unit.into());
14
15    for node in target_nodes {
16        let source_unit_part = node.source_unit_part().unwrap();
17
18        if let pt::SourceUnitPart::ContractDefinition(contract_definition) = source_unit_part {
19            let mut variable_sizes: Vec<u16> = vec![];
20
21            for part in contract_definition.clone().parts {
22                if let pt::ContractPart::VariableDefinition(box_variable_definition) = part {
23                    variable_sizes.push(utils::get_type_size(box_variable_definition.ty));
24                }
25            }
26
27            //Cache a version of variable sizes that is unordered
28            let unordered_variable_sizes = variable_sizes.clone();
29
30            //Sort the variable sizes
31            variable_sizes.sort();
32
33            //If the ordered version is smaller than the unordered, add loc
34            if utils::storage_slots_used(unordered_variable_sizes)
35                > utils::storage_slots_used(variable_sizes)
36            {
37                optimization_locations.insert(contract_definition.loc);
38            }
39        }
40    }
41
42    optimization_locations
43}
44
45#[test]
46fn test_pack_storage_variables_optimization() {
47    // Optimal packing
48    let contract = r#"
49    contract Contract {
50        uint256 num0;
51        uint256 num1;
52        uint256 num2;
53        bool bool0;
54        bool bool1;
55    }
56    "#;
57
58    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
59    let optimization_locations = pack_storage_variables_optimization(source_unit);
60    assert_eq!(optimization_locations.len(), 0);
61
62    // Cannot pack better, 2x bytes24 don't fit in a slot
63    let contract = r#"
64        contract Contract {
65            bytes24 b0;
66            uint256 num0;
67            bytes24 b1;
68        }
69        "#;
70
71    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
72    let optimization_locations = pack_storage_variables_optimization(source_unit);
73    assert_eq!(optimization_locations.len(), 0);
74
75    // Cannot pack better, bool are stored with uint8 so cannot move bo1
76    let contract = r#"
77        contract Contract {
78            bytes28 b0;
79            uint8 num0;
80            uint8 num1;
81            uint8 num2;
82            bool bo0;
83            uint256 num3;
84            bool bo1;
85        }
86        "#;
87
88    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
89    let optimization_locations = pack_storage_variables_optimization(source_unit);
90    assert_eq!(optimization_locations.len(), 0);
91
92    // Suboptimal, can be packed better
93    let contract = r#"
94    contract Contract {
95        uint256 num0;
96        uint256 num1;
97        bool bool0;
98        uint256 num2;
99        bool bool1;
100    }
101    "#;
102
103    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
104    let optimization_locations = pack_storage_variables_optimization(source_unit);
105    assert_eq!(optimization_locations.len(), 1);
106
107    // Suboptimal, can be packed better (owner,bool,num0);
108    let contract = r#"
109    contract Contract {
110        address owner;
111        uint256 num0;
112        bool bool0;
113    }
114    "#;
115
116    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
117    let optimization_locations = pack_storage_variables_optimization(source_unit);
118    assert_eq!(optimization_locations.len(), 1);
119
120    // Suboptimal, can be packed better (owner,num1,b0,num0)
121    let contract = r#"
122        contract Contract {
123            address owner; // 160 bits
124            uint256 num0;  // 256 bits
125            bytes4 b0;     // 32 bits
126            uint64 num1;   // 64 bits
127        }
128        "#;
129
130    let source_unit = solang_parser::parse(contract, 0).unwrap().0;
131    let optimization_locations = pack_storage_variables_optimization(source_unit);
132    assert_eq!(optimization_locations.len(), 1);
133}