reaching_values.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SPDX-License-Identifier: Apache-2.0
  2. use super::expression_values::expression_values;
  3. use super::value::Value;
  4. use super::{track, Variables, MAX_VALUES};
  5. use crate::codegen::cfg::{ControlFlowGraph, Instr};
  6. use crate::sema::ast::Namespace;
  7. use std::collections::{HashMap, HashSet};
  8. /// Step through a block, and calculate the reaching values for all the variables
  9. pub(super) fn reaching_values(
  10. block_no: usize,
  11. cfg: &ControlFlowGraph,
  12. vars: &mut Variables,
  13. block_vars: &mut HashMap<usize, Variables>,
  14. ns: &Namespace,
  15. ) {
  16. // We should merge the incoming set of variables with the existing ones. If there
  17. // are no changes, then we cease traversing the cfg. The rules are:
  18. // - If there are more than MAX_VALUES entries in the result, make the set the unknown set
  19. // - If either the existing set or the incoming set contains unknown, make set the unknown set
  20. // - If there are no changes to the existing set, record this
  21. // - This is a very hot code path. This needs to be _FAST_ else compilation time quickly explodes
  22. if let Some(map) = block_vars.get_mut(&block_no) {
  23. let mut changes = false;
  24. for (var_no, set) in vars.iter() {
  25. changes |= update_map(var_no, set, map);
  26. }
  27. if !changes {
  28. // no need to do this again
  29. return;
  30. }
  31. } else {
  32. block_vars.insert(block_no, vars.clone());
  33. }
  34. for instr in &cfg.blocks[block_no].instr {
  35. transfer(instr, vars, ns);
  36. match instr {
  37. Instr::Branch { block } => {
  38. // must be last in the block
  39. reaching_values(*block, cfg, vars, block_vars, ns);
  40. }
  41. Instr::BranchCond {
  42. cond,
  43. true_block,
  44. false_block,
  45. } => {
  46. // must be last in the block
  47. let v = expression_values(cond, vars, ns);
  48. if v.len() == 1 {
  49. let v = v.iter().next().unwrap();
  50. // if we know the value of the condition, follow that path
  51. if v.known_bits[0] {
  52. reaching_values(
  53. if v.value[0] {
  54. *true_block
  55. } else {
  56. *false_block
  57. },
  58. cfg,
  59. vars,
  60. block_vars,
  61. ns,
  62. );
  63. continue;
  64. }
  65. }
  66. // we don't know the value of the condition. Follow both paths
  67. let mut vars_copy = vars.clone();
  68. reaching_values(*true_block, cfg, &mut vars_copy, block_vars, ns);
  69. reaching_values(*false_block, cfg, vars, block_vars, ns);
  70. }
  71. Instr::AbiDecode {
  72. exception_block: Some(block),
  73. ..
  74. } => {
  75. let mut vars = vars.clone();
  76. reaching_values(*block, cfg, &mut vars, block_vars, ns);
  77. }
  78. _ => (),
  79. }
  80. }
  81. }
  82. /// Update the Variable's map based on the incoming set of values. Returns true if there was any
  83. /// changes in the set.
  84. /// There is a discussion to improve this function: https://github.com/hyperledger/solang/issues/934
  85. fn update_map(var_no: &usize, set: &HashSet<Value>, map: &mut Variables) -> bool {
  86. return if let Some(existing) = map.get_mut(var_no) {
  87. if existing.iter().next().map_or(false, |v| v.all_unknown()) {
  88. // If we already think it is unknown, nothing can improve on that
  89. false
  90. } else if let Some(v) = set.iter().find(|v| v.all_unknown()) {
  91. // If we are merging an unknown value, set the entire value set to unknown
  92. let mut set = HashSet::new();
  93. set.insert(v.clone());
  94. map.insert(*var_no, set);
  95. true
  96. } else {
  97. let mut changes = false;
  98. for v in set {
  99. if !existing.contains(v) {
  100. existing.insert(v.clone());
  101. changes = true;
  102. }
  103. }
  104. if existing.len() > MAX_VALUES {
  105. let bits = existing.iter().next().unwrap().bits;
  106. let mut set = HashSet::new();
  107. set.insert(Value::unknown(bits));
  108. changes = true;
  109. map.insert(*var_no, set);
  110. }
  111. changes
  112. }
  113. } else {
  114. // We have no existing set. Create one but folding unknown
  115. if set.len() > MAX_VALUES || set.iter().any(|v| v.all_unknown()) {
  116. let bits = set.iter().next().unwrap().bits;
  117. let mut set = HashSet::new();
  118. set.insert(Value::unknown(bits));
  119. map.insert(*var_no, set);
  120. } else {
  121. map.insert(*var_no, set.clone());
  122. }
  123. true
  124. };
  125. }
  126. /// For a given instruction, calculate the new reaching values
  127. pub(super) fn transfer(instr: &Instr, vars: &mut Variables, ns: &Namespace) {
  128. match instr {
  129. Instr::Set { res, expr, .. } => {
  130. let v = expression_values(expr, vars, ns);
  131. vars.insert(*res, v);
  132. }
  133. Instr::AbiDecode { res, tys, .. } => {
  134. for (i, var_no) in res.iter().enumerate() {
  135. let ty = &tys[i].ty;
  136. if track(ty) {
  137. let mut set = HashSet::new();
  138. let bits = ty.bits(ns) as usize;
  139. set.insert(Value::unknown(bits));
  140. vars.insert(*var_no, set);
  141. }
  142. }
  143. }
  144. Instr::Call {
  145. res, return_tys, ..
  146. } => {
  147. for (i, var_no) in res.iter().enumerate() {
  148. let mut set = HashSet::new();
  149. let ty = &return_tys[i];
  150. if track(ty) {
  151. let bits = ty.bits(ns) as usize;
  152. set.insert(Value::unknown(bits));
  153. vars.insert(*var_no, set);
  154. }
  155. }
  156. }
  157. Instr::PopStorage { res: Some(res), .. } => {
  158. let mut set = HashSet::new();
  159. set.insert(Value::unknown(8));
  160. vars.insert(*res, set);
  161. }
  162. Instr::PopMemory { res, ty, .. } => {
  163. if track(ty) {
  164. let mut set = HashSet::new();
  165. let bits = ty.bits(ns) as usize;
  166. set.insert(Value::unknown(bits));
  167. vars.insert(*res, set);
  168. }
  169. }
  170. _ => (),
  171. }
  172. }