advanced.py 8.7 KB

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