advanced.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. print s
  143. rs, rt = s
  144. a, b = register[rs], register[rt]
  145. if s.name == 'mult':
  146. if not a or not b:
  147. # Multiplication by 0
  148. hi = lo = to_hex(0)
  149. message = 'Multiplication by 0: %d * 0' % (b if a else a)
  150. elif a == 1:
  151. # Multiplication by 1
  152. hi = to_hex(0)
  153. lo = to_hex(b)
  154. message = 'Multiplication by 1: %d * 1' % b
  155. elif b == 1:
  156. # Multiplication by 1
  157. hi = to_hex(0)
  158. lo = to_hex(a)
  159. message = 'Multiplication by 1: %d * 1' % a
  160. else:
  161. # Calculate result and fill Hi/Lo registers
  162. result = a * b
  163. binary = bin(result)[2:]
  164. binary = '0' * (64 - len(binary)) + binary
  165. hi = int(binary[:32], base=2)
  166. lo = int(binary[32:], base=2)
  167. message = 'Constant multiplication: %d * %d = %d' \
  168. % (a, b, result)
  169. # Replace the multiplication with two immidiate loads to the
  170. # Hi/Lo registers
  171. block.replace(1, [S('command', 'li', '$hi', hi),
  172. S('command', 'li', '$lo', li)],
  173. message=message)
  174. elif s.name == 'div':
  175. lo, hi = divmod(rs, rt)
  176. register['$lo'], register['$hi'] = lo, hi
  177. known += [('$lo', lo), ('$hi', hi)]
  178. changed = True
  179. elif s.name in ['addu', 'subu']:
  180. # Addition/subtraction with constants
  181. rd, rs, rt = s
  182. rs_known = rs in register
  183. rt_known = rt in register
  184. if (rs_known or isinstance(rs, int)) and \
  185. (rt_known or isinstance(rt, int)):
  186. # a = 5 -> b = 15
  187. # c = 10
  188. # b = a + c
  189. rs_val = register[rs] if rs_known else rs
  190. rt_val = register[rt] if rt_known else rt
  191. if s.name == 'addu':
  192. result = rs_val + rt_val
  193. message = 'Constant addition: %d + %d = %d' \
  194. % (rs_val, rt_val, result)
  195. if s.name == 'subu':
  196. result = rs_val - rt_val
  197. message = 'Constant subtraction: %d - %d = %d' \
  198. % (rs_val, rt_val, result)
  199. block.replace(1, [S('command', 'li', rd, to_hex(result))],
  200. message=message)
  201. register[rd] = result
  202. known.append((rd, result))
  203. changed = True
  204. continue
  205. if rt_known:
  206. # a = 10 -> b = c + 10
  207. # b = c + a
  208. s[2] = register[rt]
  209. changed = True
  210. elif rs_known and s.name == 'addu':
  211. # c = 10 -> b = a + 10
  212. # b = c + a
  213. s[1] = rt
  214. s[2] = register[rs]
  215. changed = True
  216. if s[2] == 0:
  217. # Addition/subtraction by 0
  218. message = '%s by 0: %s * 1' % ('Addition' if s.name == 'addu' \
  219. else 'Substraction', s[1])
  220. block.replace(1, [S('command', 'move', rd, s[1])], \
  221. message=message)
  222. else:
  223. for reg in s.get_def():
  224. if reg in register:
  225. # Known register is overwritten, remove its value
  226. del register[reg]
  227. known.append((reg, 'unknown'))
  228. if block.debug and len(known):
  229. s.set_inline_comment(','.join([' %s = %s' % k for k in known]))
  230. return changed
  231. def copy_propagation(block):
  232. """
  233. Unpack a move instruction, by replacing its destination
  234. address with its source address in the code following the move instruction.
  235. This way, the move statement might be a target for dead code elimination.
  236. move $regA, $regB move $regA, $regB
  237. ... ...
  238. Code not writing $regA, -> ...
  239. $regB ...
  240. ... ...
  241. addu $regC, $regA, ... addu $regC, $regB, ...
  242. """
  243. moves_from = []
  244. moves_to = []
  245. changed = False
  246. block.reset()
  247. while not block.end():
  248. s = block.read()
  249. if s.is_command('move') and s[0] not in moves_to:
  250. # Add this move to the lists, because it is not yet there.
  251. moves_from.append(s[1])
  252. moves_to.append(s[0])
  253. elif s.is_command('move') and s[0] in moves_to:
  254. # This move is already in the lists, so only update it
  255. for i in xrange(len(moves_to)):
  256. if moves_to[i] == s[0]:
  257. moves_from[i] = s[1]
  258. continue
  259. elif (len(s) == 3 or s.is_command('mlfo') or s.is_load()) \
  260. and (s[0] in moves_to or s[0] in moves_from):
  261. # One of the registers gets overwritten, so remove the data from
  262. # the list.
  263. i = 0
  264. while i < len(moves_to):
  265. if moves_to[i] == s[0] or moves_to[i] == s[1]:
  266. del moves_to[i]
  267. del moves_from[i]
  268. else:
  269. i += 1
  270. elif len(s) == 3 and (s[1] in moves_to or s[2] in moves_to):
  271. # Check where the result of the move is used and replace it with
  272. # the original variable.
  273. for i in xrange(len(moves_to)):
  274. if s[1] == moves_to[i]:
  275. s[1] = moves_from[i]
  276. continue
  277. if s[2] == moves_to[i]:
  278. s[2] = moves_from[i]
  279. continue
  280. changed = True
  281. return changed
  282. def algebraic_transformations(block):
  283. """
  284. Change ineffective or useless algebraic expressions. Handled are:
  285. - x = y + 0 -> x = y
  286. - x = y - 0 -> x = y
  287. - x = y * 1 -> x = y
  288. - x = y * 0 -> x = 0
  289. - x = y * 2 -> x = x << 1
  290. """
  291. changed = False
  292. block.reset()
  293. while not block.end():
  294. s = block.read()
  295. if (s.is_command('addu') or s.is_command('subu')) and s[2] == 0:
  296. block.replace(1, [S('command', 'move', s[0], s[1])])
  297. changed = True
  298. elif s.is_command('mult'):
  299. mflo = block.peek()
  300. if mflo.is_command('mflo'):
  301. if s[1] == 1:
  302. block.replace(2, [S('command', 'move', mflo[0], s[0])])
  303. changed = True
  304. continue
  305. elif s[1] == 0:
  306. block.replace(2, [S('command', 'li', '$1', to_hex(0))])
  307. changed = True
  308. continue
  309. shift_amount = log(s[1], 2)
  310. if shift_amount.is_integer():
  311. new_command = S('command', 'sll', \
  312. mflo[0], s[0], \
  313. int(shift_amount))
  314. block.replace(2, [new_command])
  315. changed = True
  316. return changed
  317. def eliminate_dead_code(block):
  318. """
  319. Dead code elimination:
  320. TODO: example...
  321. The algorithm used is as follows:
  322. - Traverse through the statements in reverse order.
  323. - If the statement definition is dead, remove it. A variable is dead if it
  324. is not used in the rest of the block, and is not in the `out' set of the
  325. block.
  326. """
  327. # TODO: Finish
  328. changed = False
  329. unused = set()
  330. for s in reversed(block):
  331. for reg in s.get_def():
  332. if reg in unused:
  333. # Statement is redefined later, so this statement is useless
  334. if block.debug:
  335. s.stype = 'comment'
  336. s.options['block'] = False
  337. s.name = ' Dead code: %s %s' \
  338. % (s.name, ', '.join(map(str, s)))
  339. else:
  340. s.remove = True
  341. else:
  342. unused.add(reg)
  343. unused -= set(s.get_use())
  344. if not block.debug:
  345. block.apply_filter(lambda s: not hasattr(s, 'remove'))
  346. return changed