advanced.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. from src.statement import Statement as S
  2. from math import log
  3. def reg_can_be_used_in(reg, block, start, end):
  4. """Check if a register addres safely be used in a block section using local
  5. dataflow analysis."""
  6. # Check if the register used or defined in the block section
  7. for s in block[start:end]:
  8. if s.uses(reg) or s.defines(reg):
  9. return False
  10. # Check if the register is used inside the block after the specified
  11. # section, without having been re-assigned first
  12. for s in block[end:]:
  13. if s.uses(reg):
  14. return False
  15. elif s.defines(reg):
  16. return True
  17. return reg not in block.live_out
  18. def find_free_reg(block, start, end):
  19. """Find a temporary register that is free in a given list of statements."""
  20. for i in xrange(8, 16):
  21. tmp = '$%d' % i
  22. if reg_can_be_used_in(tmp, block, start, end):
  23. return tmp
  24. raise Exception('No temporary register is available.')
  25. def eliminate_common_subexpressions(block):
  26. """
  27. Common subexpression elimination:
  28. x = a + b -> u = a + b
  29. y = a + b x = u
  30. y = u
  31. The algorithm used is as follows:
  32. - Traverse through the statements.
  33. - If the statement can be possibly be eliminated, walk further collecting
  34. all other occurrences of the expression until one of the arguments is
  35. assigned in a statement, or the start of the block has been reached.
  36. - If one or more occurrences were changed, insert the expression with a new
  37. destination address before the last changed occurrence and change all
  38. occurrences to a move instruction from that address.
  39. """
  40. changed = False
  41. block.reset()
  42. while not block.end():
  43. s = block.read()
  44. if s.is_arith():
  45. pointer = block.pointer
  46. occurrences = [pointer - 1]
  47. args = s[1:]
  48. # Collect similar statements
  49. while not block.end():
  50. s2 = block.read()
  51. if not s2.is_command():
  52. continue
  53. # Stop if one of the arguments is assigned
  54. if len(s2) and s2[0] in args:
  55. break
  56. # Replace a similar expression by a move instruction
  57. if s2.name == s.name and s2[1:] == args:
  58. occurrences.append(block.pointer - 1)
  59. if len(occurrences) > 1:
  60. new_reg = find_free_reg(block, occurrences[0], occurrences[-1])
  61. # Replace all occurrences with a move statement
  62. message = 'Common subexpression reference: %s %s' \
  63. % (s.name, ', '.join(map(str, [new_reg] + s[1:])))
  64. for occurrence in occurrences:
  65. rd = block[occurrence][0]
  66. block.replace(1, [S('command', 'move', rd, new_reg)], \
  67. start=occurrence, message=message)
  68. # Insert the calculation before the original with the new
  69. # destination address
  70. message = 'Common subexpression: %s %s' \
  71. % (s.name, ', '.join(map(str, s)))
  72. block.insert(S('command', s.name, *([new_reg] + args)), \
  73. index=occurrences[0], message=message)
  74. changed = True
  75. # Reset pointer to continue from the original statement
  76. block.pointer = pointer
  77. return changed
  78. def to_hex(value):
  79. """Create the hexadecimal string of an integer."""
  80. return '0x%08x' % value
  81. def fold_constants(block):
  82. """
  83. Constant folding:
  84. x = 3 + 5 -> x = 8
  85. y = x * 2 y = 16
  86. To keep track of constant values, the following assumptions are made:
  87. - An immediate load defines a register value:
  88. li $reg, XX -> register[$reg] = XX
  89. - Integer variable definition is of the following form:
  90. li $reg, XX -> constants[VAR] = XX
  91. sw $reg, VAR -> register[$reg] = XX
  92. - When a variable is used, the following happens:
  93. lw $reg, VAR -> register[$reg] = constants[VAR]
  94. """
  95. changed = False
  96. # Variable values
  97. constants = {}
  98. # Current known values in register
  99. register = {}
  100. block.reset()
  101. while not block.end():
  102. s = block.read()
  103. known = []
  104. if not s.is_command():
  105. continue
  106. if s.name == 'li':
  107. # Save value in register
  108. if not isinstance(s[1], int): # Negative numbers are stored as int
  109. register[s[0]] = int(s[1], 16)
  110. else:
  111. register[s[0]] = s[1]
  112. known.append((s[0], register[s[0]]))
  113. elif s.name == 'move' and s[0] in register:
  114. reg_to, reg_from = s
  115. if reg_from in register:
  116. # Other value is also known, copy its value
  117. register[reg_to] = register[reg_from]
  118. known.append((reg_to, register[reg_to]))
  119. else:
  120. # Other value is unknown, delete the value
  121. del register[reg_to]
  122. known.append((reg_to, 'unknown'))
  123. elif s.name == 'sw' and s[0] in register:
  124. # Constant variable definition, e.g. 'int a = 1;'
  125. constants[s[1]] = register[s[0]]
  126. known.append((s[1], register[s[0]]))
  127. elif s.name == 'lw' and s[1] in constants:
  128. # Usage of variable with constant value
  129. register[s[0]] = constants[s[1]]
  130. known.append((s[0], register[s[0]]))
  131. elif s.name == 'mflo' and '$lo' in register:
  132. # Move of `Lo' register to another register
  133. register[s[0]] = register['$lo']
  134. known.append((s[0], register[s[0]]))
  135. elif s.name == 'mfhi' and '$hi' in register:
  136. # Move of `Hi' register to another register
  137. register[s[0]] = register['$hi']
  138. known.append((s[0], register[s[0]]))
  139. elif s.name in ['mult', 'div'] \
  140. and s[0]in register and s[1] in register:
  141. # Multiplication/division with constants
  142. rs, rt = s
  143. a, b = register[rs], register[rt]
  144. if s.name == 'mult':
  145. if not a or not b:
  146. # Multiplication by 0
  147. hi = lo = to_hex(0)
  148. message = 'Multiplication by 0: %d * 0' % (b if a else a)
  149. elif a == 1:
  150. # Multiplication by 1
  151. hi = to_hex(0)
  152. lo = to_hex(b)
  153. message = 'Multiplication by 1: %d * 1' % b
  154. elif b == 1:
  155. # Multiplication by 1
  156. hi = to_hex(0)
  157. lo = to_hex(a)
  158. message = 'Multiplication by 1: %d * 1' % a
  159. else:
  160. # Calculate result and fill Hi/Lo registers
  161. result = a * b
  162. binary = bin(result)[2:]
  163. binary = '0' * (64 - len(binary)) + binary
  164. hi = int(binary[:32], base=2)
  165. lo = int(binary[32:], base=2)
  166. message = 'Constant multiplication: %d * %d = %d' \
  167. % (a, b, result)
  168. # Replace the multiplication with two immidiate loads to the
  169. # Hi/Lo registers
  170. block.replace(1, [S('command', 'li', '$hi', hi),
  171. S('command', 'li', '$lo', li)],
  172. message=message)
  173. elif s.name == 'div':
  174. lo, hi = divmod(rs, rt)
  175. register['$lo'], register['$hi'] = lo, hi
  176. known += [('$lo', lo), ('$hi', hi)]
  177. changed = True
  178. elif s.name in ['addu', 'subu']:
  179. # Addition/subtraction with constants
  180. rd, rs, rt = s
  181. rs_known = rs in register
  182. rt_known = rt in register
  183. if (rs_known or isinstance(rs, int)) and \
  184. (rt_known or isinstance(rt, int)):
  185. # a = 5 -> b = 15
  186. # c = 10
  187. # b = a + c
  188. rs_val = register[rs] if rs_known else rs
  189. rt_val = register[rt] if rt_known else rt
  190. if s.name == 'addu':
  191. result = rs_val + rt_val
  192. message = 'Constant addition: %d + %d = %d' \
  193. % (rs_val, rt_val, result)
  194. if s.name == 'subu':
  195. result = rs_val - rt_val
  196. message = 'Constant subtraction: %d - %d = %d' \
  197. % (rs_val, rt_val, result)
  198. block.replace(1, [S('command', 'li', rd, to_hex(result))],
  199. message=message)
  200. register[rd] = result
  201. known.append((rd, result))
  202. changed = True
  203. continue
  204. if rt_known:
  205. # a = 10 -> b = c + 10
  206. # b = c + a
  207. s[2] = register[rt]
  208. changed = True
  209. elif rs_known and s.name == 'addu':
  210. # c = 10 -> b = a + 10
  211. # b = c + a
  212. s[1] = rt
  213. s[2] = register[rs]
  214. changed = True
  215. if s[2] == 0:
  216. # Addition/subtraction by 0
  217. message = '%s by 0: %s * 1' % ('Addition' if s.name == 'addu' \
  218. else 'Substraction', s[1])
  219. block.replace(1, [S('command', 'move', rd, s[1])], \
  220. message=message)
  221. else:
  222. for reg in s.get_def():
  223. if reg in register:
  224. # Known register is overwritten, remove its value
  225. del register[reg]
  226. known.append((reg, 'unknown'))
  227. if block.debug and len(known):
  228. s.set_inline_comment(','.join([' %s = %s' % k for k in known]))
  229. return changed
  230. def copy_propagation(block):
  231. """
  232. Unpack a move instruction, by replacing its destination
  233. address with its source address in the code following the move instruction.
  234. This way, the move statement might be a target for dead code elimination.
  235. move $regA, $regB move $regA, $regB
  236. ... ...
  237. Code not writing $regA, -> ...
  238. $regB ...
  239. ... ...
  240. addu $regC, $regA, ... addu $regC, $regB, ...
  241. """
  242. moves_from = []
  243. moves_to = []
  244. changed = False
  245. block.reset()
  246. while not block.end():
  247. s = block.read()
  248. if s.is_command('move') and s[0] not in moves_to:
  249. # Add this move to the lists, because it is not yet there.
  250. moves_from.append(s[1])
  251. moves_to.append(s[0])
  252. elif s.is_command('move') and s[0] in moves_to:
  253. # This move is already in the lists, so only update it
  254. for i in xrange(len(moves_to)):
  255. if moves_to[i] == s[0]:
  256. moves_from[i] = s[1]
  257. continue
  258. elif (len(s) == 3 or s.is_command('mlfo') or s.is_load()) \
  259. and (s[0] in moves_to or s[0] in moves_from):
  260. # One of the registers gets overwritten, so remove the data from
  261. # the list.
  262. i = 0
  263. while i < len(moves_to):
  264. if moves_to[i] == s[0] or moves_to[i] == s[1]:
  265. del moves_to[i]
  266. del moves_from[i]
  267. else:
  268. i += 1
  269. elif len(s) == 3 and (s[1] in moves_to or s[2] in moves_to):
  270. # Check where the result of the move is used and replace it with
  271. # the original variable.
  272. for i in xrange(len(moves_to)):
  273. if s[1] == moves_to[i]:
  274. s[1] = moves_from[i]
  275. continue
  276. if s[2] == moves_to[i]:
  277. s[2] = moves_from[i]
  278. continue
  279. changed = True
  280. return changed
  281. def algebraic_transformations(block):
  282. """
  283. Change ineffective or useless algebraic expressions. Handled are:
  284. - x = y + 0 -> x = y
  285. - x = y - 0 -> x = y
  286. - x = y * 1 -> x = y
  287. - x = y * 0 -> x = 0
  288. - x = y * 2 -> x = x << 1
  289. """
  290. changed = False
  291. block.reset()
  292. while not block.end():
  293. s = block.read()
  294. if (s.is_command('addu') or s.is_command('subu')) and s[2] == 0:
  295. block.replace(1, [S('command', 'move', s[0], s[1])])
  296. changed = True
  297. elif s.is_command('mult'):
  298. mflo = block.peek()
  299. if mflo.is_command('mflo'):
  300. if s[1] == 1:
  301. block.replace(2, [S('command', 'move', mflo[0], s[0])])
  302. changed = True
  303. continue
  304. elif s[1] == 0:
  305. block.replace(2, [S('command', 'li', '$1', to_hex(0))])
  306. changed = True
  307. continue
  308. shift_amount = log(s[1], 2)
  309. if shift_amount.is_integer():
  310. new_command = S('command', 'sll', \
  311. mflo[0], s[0], \
  312. int(shift_amount))
  313. block.replace(2, [new_command])
  314. changed = True
  315. return changed
  316. def eliminate_dead_code(block):
  317. """
  318. Dead code elimination:
  319. TODO: example...
  320. The algorithm used is as follows:
  321. - Traverse through the statements in reverse order.
  322. - If the statement definition is dead, remove it. A variable is dead if it
  323. is not used in the rest of the block, and is not in the `out' set of the
  324. block.
  325. """
  326. # TODO: Finish
  327. changed = False
  328. unused = set()
  329. for s in reversed(block):
  330. for reg in s.get_def():
  331. if reg in unused:
  332. # Statement is redefined later, so this statement is useless
  333. if block.debug:
  334. s.stype = 'comment'
  335. s.options['block'] = False
  336. s.name = ' Dead code: %s %s' \
  337. % (s.name, ', '.join(map(str, s)))
  338. else:
  339. s.remove = True
  340. else:
  341. unused.add(reg)
  342. unused -= set(s.get_use())
  343. if not block.debug:
  344. block.apply_filter(lambda s: not hasattr(s, 'remove'))
  345. return changed