advanced.py 8.1 KB

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