optimizer.rst 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. Optimizer Passes
  2. ================
  3. Solang generates its own internal IR, before the LLVM IR is generated. This internal IR allows us to do
  4. several optimizations which LLVM cannot do, since it is not aware of higher-level language constructs.
  5. Arithmetic of large integers (larger than 64 bit) has special handling, since LLVM cannot generate them.
  6. So we need to do our own optimizations for these types, and we cannot rely on LLVM.
  7. .. _constant-folding:
  8. Constant Folding Pass
  9. ---------------------
  10. There is a constant folding (also called constant propagation) pass done, before all the other passes. This
  11. helps arithmetic of large types, and also means that the functions are constant folded when their arguments
  12. are constant. For example:
  13. .. code-block:: solidity
  14. bytes32 hash = keccak256('foobar');
  15. This is evaluated at compile time. You can see this in the Visual Studio Code extension by hover over `hash`;
  16. the hover will tell you the value of the hash.
  17. .. _strength-reduce:
  18. Strength Reduction Pass
  19. -----------------------
  20. Strength reduction is when expensive arithmetic is replaced with cheaper ones. So far, the following types
  21. of arithmetic may be replaced:
  22. - 256 or 128 bit multiply maybe replaced by 64 bit multiply or shift
  23. - 256 or 128 bit divide maybe replaced by 64 bit divide or shift
  24. - 256 or 128 bit modulo maybe replaced by 64 bit modulo or bitwise and
  25. .. code-block:: solidity
  26. contract test {
  27. function f() public {
  28. for (uint i = 0; i < 10; i++) {
  29. // this multiply can be done with a 64 bit instruction
  30. g(i * 100));
  31. }
  32. }
  33. function g(uint256 v) internal {
  34. // ...
  35. }
  36. }
  37. Solang uses reaching definitions to track the known bits of the variables; here solang knows that i can have
  38. the values 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 and the other operand is always 100. So, the multiplication can be
  39. done using a single 64 bit multiply instruction. If you hover over the ``*`` in the Visual Studio Code you
  40. will see this noted.
  41. .. _dead-storage:
  42. Dead Storage pass
  43. -----------------
  44. Loading from contract storage, or storing to contract storage is expensive. This optimization removes any
  45. redundant load from and store to contract storage. If the same variable is read twice, then the value from
  46. the first load is re-used. Similarly, if there is are two successive stores to the same variable, the first
  47. one is removed as it is redundant. For example:
  48. .. code-block:: solidity
  49. contract test {
  50. int a;
  51. // this function reads a twice; this can be reduced to one load
  52. function redundant_load() public returns (int) {
  53. return a + a;
  54. }
  55. // this function writes to contract storage thrice. This can be reduced to one
  56. function redundant_store() public {
  57. delete a;
  58. a = 1;
  59. a = 2;
  60. }
  61. }
  62. This optimization pass can be disabled by running `solang --no-dead-storage`. You can see the difference between
  63. having this optimization pass on by comparing the output of `solang --no-dead-storage --emit cfg foo.sol` with
  64. `solang --emit cfg foo.sol`.
  65. .. _vector-to-slice:
  66. Vector to Slice Pass
  67. --------------------
  68. A `bytes` or `string` variable can be stored in a vector, which is a modifyable in-memory buffer, and a slice
  69. which is a pointer to readonly memory and an a length. Since a vector is modifyable, each instance requires
  70. a allocation. For example:
  71. .. code-block:: solidity
  72. contract test {
  73. function can_be_slice() public {
  74. // v can just be a pointer to constant memory and an a length indicator
  75. string v = "Hello, World!";
  76. print(v);
  77. }
  78. function must_be_vector() public {
  79. // if v is a vector, then it needs to allocated and default value copied.
  80. string v = "Hello, World!";
  81. // bs is copied by reference is now modifyable
  82. bytes bs = v;
  83. bs[1] = 97;
  84. print(v);
  85. }
  86. }
  87. This optimization pass can be disabled by running `solang --no-vector-to-slice`. You can see the difference between
  88. having this optimization pass on by comparing the output of `solang --no-vector-to-slice --emit cfg foo.sol` with
  89. `solang --emit cfg foo.sol`.
  90. .. _unused-variable-elimination:
  91. Unused Variable Elimination
  92. ----------------------------
  93. During the semantic analysis, Solang detects unused variables and raises warnings for them.
  94. During codegen, we remove all assignments that have been made to this unused variable. There is an example below:
  95. .. code-block:: solidity
  96. contract test {
  97. function test1(int a) public pure returns (int) {
  98. int x = 5;
  99. x++;
  100. if (a > 0) {
  101. x = 5;
  102. }
  103. a = (x=3) + a*4;
  104. return a;
  105. }
  106. }
  107. The variable 'x' will be removed from the function, as it has never been used. The removal won't affect any
  108. expressions inside the function.
  109. .. _common-subexpression-elimination:
  110. Common Subexpression Elimination
  111. ---------------------------------
  112. Solang performs common subexpression elimination by doing two passes over the CFG (Control
  113. Flow Graph). During the first one, it builds a graph to track existing expressions and detect repeated ones.
  114. During the second pass, it replaces the repeated expressions by a temporary variable, which assumes the value
  115. of the expression. To disable this feature, use `solang --no-cse`.
  116. Check out the example below. It contains multiple common subexpressions:
  117. .. code-block:: solidity
  118. contract test {
  119. function csePass(int a, int b) public pure returns (int) {
  120. int x = a*b-5;
  121. if (x > 0) {
  122. x = a*b-19;
  123. } else {
  124. x = a*b*a;
  125. }
  126. return x+a*b;
  127. }
  128. }
  129. The expression `a*b` is repeated throughout the function and will be saved to a temporary variable.
  130. This temporary will be placed wherever there is an expression `a*b`. You can see the pass in action when you compile
  131. this contract and check the CFG, using `solang --emit cfg`.