advanced.py 7.5 KB

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