advanced.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. from src.statement import Statement as S
  2. from math import log
  3. def create_variable():
  4. return '$15'
  5. def eliminate_common_subexpressions(block):
  6. """
  7. Common subexpression elimination:
  8. x = a + b -> u = a + b
  9. y = a + b x = u
  10. y = u
  11. The algorithm used is as follows:
  12. - Traverse through the statements in reverse order.
  13. - If the statement can be possibly be eliminated, walk further collecting
  14. all other occurrences of the expression until one of the arguments is
  15. assigned in a statement, or the start of the block has been reached.
  16. - If one or more occurrences were found, insert the expression with a new
  17. destination address before the last found occurrence and change all
  18. occurrences to a move instruction from that address.
  19. """
  20. found = False
  21. block.reverse_statements()
  22. while not block.end():
  23. s = block.read()
  24. if s.is_arith():
  25. pointer = block.pointer
  26. last = False
  27. new_reg = False
  28. args = s[1:]
  29. # Collect similar statements
  30. while not block.end():
  31. s2 = block.read()
  32. # Stop if one of the arguments is assigned
  33. if len(s2) and s2[0] in args:
  34. break
  35. # Replace a similar expression by a move instruction
  36. if s2.name == s.name and s2[1:] == args:
  37. if not new_reg:
  38. new_reg = create_variable()
  39. block.replace(1, [S('command', 'move', s2[0], new_reg)])
  40. last = block.pointer
  41. # Insert an additional expression with a new destination address
  42. if last:
  43. block.insert(S('command', s.name, [new_reg] + args), last)
  44. found = True
  45. # Reset pointer to and continue from the original statement
  46. block.pointer = pointer
  47. block.reverse_statements()
  48. return found
  49. def to_hex(value):
  50. """Create the hexadecimal string of an integer."""
  51. return '0x%08x' % value
  52. def fold_constants(block):
  53. """
  54. Constant folding:
  55. x = 3 + 5 -> x = 8
  56. y = x * 2 y = 16
  57. To keep track of constant values, the following assumptions are made:
  58. - An immediate load defines a register value:
  59. li $reg, XX -> register[$reg] = XX
  60. - Integer variable definition is of the following form:
  61. li $reg, XX -> constants[VAR] = XX
  62. sw $reg, VAR -> register[$reg] = XX
  63. - When a variable is used, the following happens:
  64. lw $reg, VAR -> register[$reg] = constants[VAR]
  65. """
  66. found = False
  67. # Variable values
  68. constants = {}
  69. # Current known values in register
  70. register = {}
  71. while not block.end():
  72. s = block.read()
  73. if not s.is_command():
  74. continue
  75. if s.name == 'li':
  76. # Save value in register
  77. register[s[0]] = int(s[1], 16)
  78. elif s.name == 'move' and s[0] in register:
  79. reg_to, reg_from = s
  80. if reg_from in register:
  81. # Other value is also known, copy its value
  82. register[reg_to] = register[reg_to]
  83. else:
  84. # Other value is unknown, delete the value
  85. del register[reg_to]
  86. elif s.name == 'sw' and s[0] in register:
  87. # Constant variable definition, e.g. 'int a = 1;'
  88. constants[s[1]] = register[s[0]]
  89. elif s.name == 'lw' and s[1] in constants:
  90. # Usage of variable with constant value
  91. register[s[0]] = constants[s[1]]
  92. elif s.name in ['addu', 'subu', 'mult', 'div']:
  93. # Calculation with constants
  94. rd, rs, rt = s
  95. rs_known = rs in register
  96. rt_known = rt in register
  97. if rs_known and rt_known:
  98. # a = 5 -> b = 15
  99. # c = 10
  100. # b = a + c
  101. rs_val = register[rs]
  102. rt_val = register[rt]
  103. if s.name == 'addu':
  104. result = to_hex(rs_val + rt_val)
  105. if s.name == 'subu':
  106. result = to_hex(rs_val - rt_val)
  107. if s.name == 'mult':
  108. result = to_hex(rs_val * rt_val)
  109. if s.name == 'div':
  110. result = to_hex(rs_val / rt_val)
  111. block.replace(1, [S('command', 'li', result)])
  112. register[rd] = result
  113. found = True
  114. elif rt_known:
  115. # c = 10 -> b = a + 10
  116. # b = c + a
  117. s[2] = register[rt]
  118. found = True
  119. elif rs_known and s.name in ['addu', 'mult']:
  120. # a = 10 -> b = c + 10
  121. # b = c + a
  122. s[1] = rt
  123. s[2] = register[rs]
  124. found = True
  125. elif len(s) and s[0] in register:
  126. # Known register is overwritten, remove its value
  127. del register[s[0]]
  128. return found
  129. def copy_propagation(block):
  130. """
  131. Replace a variable with its original variable after a move if possible, by
  132. walking through the code, storing move operations and checking whether it
  133. changes or whether a variable can be replaced. This way, the move statement
  134. might be a target for dead code elimination.
  135. move $regA, $regB move $regA, $regB
  136. ... ...
  137. Code not writing $regA, -> ...
  138. $regB ...
  139. ... ...
  140. addu $regC, $regA, ... addu $regC, $regB, ...
  141. """
  142. moves_from = []
  143. moves_to = []
  144. changed = False
  145. while not block.end():
  146. s = block.read()
  147. if s.is_command('move') and s[0] not in moves_to:
  148. # Add this move to the lists, because it is not yet there.
  149. moves_from.append(s[1])
  150. moves_to.append(s[0])
  151. elif s.is_command('move') and s[0] in moves_to:
  152. # This move is already in the lists, so only update it
  153. for i in xrange(len(moves_to)):
  154. if moves_to[i] == s[0]:
  155. moves_from[i] = s[1]
  156. break
  157. elif len(s) == 3 and s[0] in moves_to:
  158. # The result gets overwritten, so remove the data from the list.
  159. i = 0
  160. while i < len(moves_to):
  161. if moves_to[i] == s[0]:
  162. del moves_to[i]
  163. del moves_from[i]
  164. else:
  165. i += 1
  166. elif len(s) == 3 and (s[1] in moves_to or s[2] in moves_to):
  167. # Check where the result of the move is used and replace it with
  168. # the original variable.
  169. for i in xrange(len(moves_to)):
  170. if s[1] == moves_to[i]:
  171. s[1] = moves_from[i]
  172. break
  173. if s[2] == moves_to[i]:
  174. s[2] = moves_from[i]
  175. break
  176. changed = True
  177. return changed
  178. def algebraic_transformations(block):
  179. """
  180. Change ineffective or useless algebraic transformations. Handled are:
  181. - x = y + 0 -> x = y
  182. - x = y - 0 -> x = y
  183. - x = y * 1 -> x = y
  184. - x = y * 0 -> x = 0
  185. - x = y * 2 -> x = x << 1
  186. """
  187. changed = False
  188. while not block.end():
  189. s = block.read()
  190. if (s.is_command('addu') or s.is_command('subu')) and s[2] == 0:
  191. block.replace(1, [S('command', 'move', s[0], s[1])])
  192. changed = True
  193. elif s.is_command('mult') and s[2] == 1:
  194. block.replace(1, [S('command', 'move', s[0], s[1])])
  195. changed = True
  196. elif s.is_command('mult') and s[2] == 0:
  197. block.replace(1, [S('command', 'li', '$1', to_hex(0))])
  198. changed = True
  199. elif s.is_command('mult'):
  200. shift_amount = log(s[2], 2)
  201. if shift_amount.is_integer():
  202. new_command = S('command', 'sll', s[0], s[1], shift_amount)
  203. block.replace(1, [new_command])
  204. changed = True
  205. return changed