reaching_definitions.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // SPDX-License-Identifier: Apache-2.0
  2. use super::cfg::{BasicBlock, ControlFlowGraph, Instr};
  3. use crate::codegen::Expression;
  4. use indexmap::IndexMap;
  5. use std::collections::HashSet;
  6. use std::fmt;
  7. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
  8. pub struct Def {
  9. pub block_no: usize,
  10. pub instr_no: usize,
  11. pub assignment_no: usize,
  12. }
  13. #[derive(Clone, Copy, PartialEq, Eq)]
  14. pub enum Transfer {
  15. Gen { def: Def, var_no: usize },
  16. Mod { var_no: usize },
  17. Copy { var_no: usize, src: usize },
  18. Kill { var_no: usize },
  19. }
  20. impl fmt::Display for Transfer {
  21. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  22. match self {
  23. Transfer::Gen { def, var_no } => {
  24. write!(f, "Gen %{var_no} = ({}, {})", def.block_no, def.instr_no)
  25. }
  26. Transfer::Mod { var_no } => {
  27. write!(f, "Mod %{var_no}")
  28. }
  29. Transfer::Copy { var_no, src } => {
  30. write!(f, "Copy %{var_no} from %{src}")
  31. }
  32. Transfer::Kill { var_no } => {
  33. write!(f, "Kill %{var_no}")
  34. }
  35. }
  36. }
  37. }
  38. pub type VarDefs = IndexMap<usize, IndexMap<Def, bool>>;
  39. /// Calculate all the reaching definitions for the contract. This is a flow
  40. /// analysis which is used for further optimizations
  41. pub fn find(cfg: &mut ControlFlowGraph) {
  42. // calculate the per-instruction reaching defs
  43. for (block_no, block) in cfg.blocks.iter_mut().enumerate() {
  44. block.transfers = instr_transfers(block_no, block);
  45. }
  46. let mut blocks_todo: HashSet<usize> = HashSet::new();
  47. blocks_todo.insert(0);
  48. while let Some(block_no) = blocks_todo.iter().next() {
  49. let block_no = *block_no;
  50. blocks_todo.remove(&block_no);
  51. let mut vars = cfg.blocks[block_no].defs.clone();
  52. for transfers in &cfg.blocks[block_no].transfers {
  53. apply_transfers(transfers, &mut vars);
  54. }
  55. for edge in block_edges(&cfg.blocks[block_no]) {
  56. if cfg.blocks[edge].defs != vars {
  57. blocks_todo.insert(edge);
  58. // merge incoming set
  59. for (var_no, defs) in &vars {
  60. if let Some(entry) = cfg.blocks[edge].defs.get_mut(var_no) {
  61. for (incoming_def, incoming_modified) in defs {
  62. if let Some(e) = entry.get_mut(incoming_def) {
  63. *e |= *incoming_modified;
  64. } else {
  65. entry.insert(*incoming_def, *incoming_modified);
  66. }
  67. }
  68. } else {
  69. cfg.blocks[edge].defs.insert(*var_no, defs.clone());
  70. }
  71. // If a definition from a block executed later reaches this block,
  72. // this is a loop. This is an analysis we use later at the
  73. // common subexpression elimination.
  74. for (incoming_def, _) in defs {
  75. if incoming_def.block_no >= edge {
  76. cfg.blocks[edge].loop_reaching_variables.insert(*var_no);
  77. }
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. /// Instruction defs
  85. fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
  86. let mut transfers = Vec::new();
  87. for (instr_no, instr) in block.instr.iter().enumerate() {
  88. let set_var = |var_nos: &[usize]| {
  89. let mut transfer = Vec::new();
  90. for (assignment_no, var_no) in var_nos.iter().enumerate() {
  91. transfer.insert(0, Transfer::Kill { var_no: *var_no });
  92. transfer.push(Transfer::Gen {
  93. def: Def {
  94. block_no,
  95. instr_no,
  96. assignment_no,
  97. },
  98. var_no: *var_no,
  99. });
  100. }
  101. transfer
  102. };
  103. transfers.push(match instr {
  104. Instr::Set {
  105. res,
  106. expr: Expression::Variable(_, _, src),
  107. ..
  108. } => {
  109. vec![
  110. Transfer::Kill { var_no: *res },
  111. Transfer::Copy {
  112. var_no: *res,
  113. src: *src,
  114. },
  115. ]
  116. }
  117. Instr::Set { res, .. } => set_var(&[*res]),
  118. Instr::Call { res, .. } => set_var(res),
  119. Instr::AbiDecode { res, .. } => set_var(res),
  120. Instr::LoadStorage { res, .. } | Instr::PopStorage { res: Some(res), .. } => {
  121. set_var(&[*res])
  122. }
  123. Instr::PushMemory { array, res, .. } => {
  124. let mut v = set_var(&[*res]);
  125. v.push(Transfer::Mod { var_no: *array });
  126. v
  127. }
  128. Instr::PopMemory { array, .. } => {
  129. vec![Transfer::Mod { var_no: *array }]
  130. }
  131. Instr::ExternalCall {
  132. success: Some(res), ..
  133. }
  134. | Instr::Constructor {
  135. success: None, res, ..
  136. }
  137. | Instr::ValueTransfer {
  138. success: Some(res), ..
  139. } => set_var(&[*res]),
  140. Instr::ClearStorage { storage: dest, .. }
  141. | Instr::SetStorageBytes { storage: dest, .. }
  142. | Instr::SetStorage { storage: dest, .. }
  143. | Instr::Store { dest, .. } => {
  144. let mut v = Vec::new();
  145. if let Some(var_no) = array_var(dest) {
  146. v.push(Transfer::Mod { var_no });
  147. }
  148. v
  149. }
  150. Instr::Constructor {
  151. success: Some(success),
  152. res,
  153. ..
  154. } => set_var(&[*res, *success]),
  155. _ => Vec::new(),
  156. });
  157. }
  158. transfers
  159. }
  160. fn array_var(expr: &Expression) -> Option<usize> {
  161. match expr {
  162. Expression::Variable(_, _, var_no) => Some(*var_no),
  163. Expression::Subscript(_, _, _, expr, _) | Expression::StructMember(_, _, expr, _) => {
  164. array_var(expr)
  165. }
  166. _ => None,
  167. }
  168. }
  169. pub fn apply_transfers(transfers: &[Transfer], vars: &mut IndexMap<usize, IndexMap<Def, bool>>) {
  170. for transfer in transfers {
  171. match transfer {
  172. Transfer::Kill { var_no } => {
  173. vars.remove(var_no);
  174. }
  175. Transfer::Mod { var_no } => {
  176. if let Some(entry) = vars.get_mut(var_no) {
  177. for e in entry.values_mut() {
  178. *e = true;
  179. }
  180. }
  181. }
  182. Transfer::Copy { var_no, src } => {
  183. if let Some(defs) = vars.get(src) {
  184. let defs = defs.clone();
  185. vars.insert(*var_no, defs);
  186. }
  187. }
  188. Transfer::Gen { var_no, def } => {
  189. if let Some(entry) = vars.get_mut(var_no) {
  190. entry.insert(*def, false);
  191. } else {
  192. let mut v = IndexMap::new();
  193. v.insert(*def, false);
  194. vars.insert(*var_no, v);
  195. }
  196. }
  197. }
  198. }
  199. }
  200. /// Fetch the blocks that can be executed after the block passed as argument
  201. pub(super) fn block_edges(block: &BasicBlock) -> Vec<usize> {
  202. let mut out = Vec::new();
  203. // out cfg has edge as the last instruction in a block; EXCEPT
  204. // Instr::AbiDecode() which has an edge when decoding fails
  205. for instr in &block.instr {
  206. match instr {
  207. Instr::Branch { block } => {
  208. out.push(*block);
  209. }
  210. Instr::BranchCond {
  211. true_block,
  212. false_block,
  213. ..
  214. } => {
  215. out.push(*true_block);
  216. out.push(*false_block);
  217. }
  218. Instr::AbiDecode {
  219. exception_block: Some(block),
  220. ..
  221. } => {
  222. out.push(*block);
  223. }
  224. Instr::Switch { default, cases, .. } => {
  225. out.push(*default);
  226. for (_, goto) in cases {
  227. out.push(*goto);
  228. }
  229. }
  230. _ => (),
  231. }
  232. }
  233. out
  234. }