statement.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import re
  2. class Statement:
  3. sid = 1
  4. def __init__(self, stype, name, *args, **kwargs):
  5. self.stype = stype
  6. self.name = name
  7. self.args = list(args)
  8. self.options = kwargs
  9. # Assign a unique ID to each statement
  10. self.sid = Statement.sid
  11. Statement.sid += 1
  12. def __getitem__(self, n):
  13. """Get an argument."""
  14. return self.args[n]
  15. def __setitem__(self, n, value):
  16. """Set an argument."""
  17. self.args[n] = value
  18. def __eq__(self, other):
  19. """Check if two statements are equal by comparing their type, name and
  20. arguments."""
  21. return self.stype == other.stype and self.name == other.name \
  22. and self.args == other.args
  23. def __len__(self):
  24. return len(self.args)
  25. def __str__(self): # pragma: nocover
  26. return '<Statement sid=%d type=%s name=%s args=%s>' \
  27. % (self.sid, self.stype, self.name, self.args)
  28. def __repr__(self): # pragma: nocover
  29. return str(self)
  30. def set_message(self, message):
  31. self.options['message'] = message
  32. def set_inline_comment(self, comment):
  33. self.options['comment'] = comment
  34. def has_inline_comment(self):
  35. return 'comment' in self.options and len(self.options['comment'])
  36. def is_comment(self):
  37. return self.stype == 'comment'
  38. def is_directive(self):
  39. return self.stype == 'directive'
  40. def is_label(self, name=None):
  41. return self.stype == 'label' if name == None \
  42. else self.stype == 'label' and self.name == name
  43. def is_command(self, *args):
  44. return self.stype == 'command' and (not len(args) or self.name in args)
  45. def is_jump(self):
  46. """Check if the statement is a jump."""
  47. return self.is_command() \
  48. and re.match('^j|jal|beq|bne|blez|bgtz|bltz|bgez|bc1t|bc1f$', \
  49. self.name)
  50. def is_branch(self):
  51. """Check if the statement is a branch."""
  52. return self.is_command() \
  53. and re.match('^beq|bne|blez|bgtz|bltz|bgez|bct|bcf|bc1f|bc1t$',\
  54. self.name)
  55. def is_branch_zero(self):
  56. """Check if statement is a branch that compares with zero."""
  57. return self.is_command() \
  58. and re.match('^blez|bgtz|bltz|bgez$', self.name)
  59. def is_shift(self):
  60. """Check if the statement is a shift operation."""
  61. return self.is_command() and re.match('^s(ll|rl|ra)$', self.name)
  62. def is_load(self):
  63. """Check if the statement is a load instruction."""
  64. return self.is_command() and self.name in ['lw', 'li', 'dlw', 'l.s', \
  65. 'l.d']
  66. def is_store(self):
  67. """Check if the statement is a store instruction."""
  68. return self.is_command() and self.name in ['sw', 'sb', 's.d', 'dsw', \
  69. 's.s', 's.b']
  70. def is_arith(self):
  71. """Check if the statement is an arithmetic operation."""
  72. return self.is_command() \
  73. and re.match('^s(ll|rl|ra)'
  74. + '|(mfhi|mflo|abs|neg|and|[xn]?or)'
  75. + '|(add|sub|slt)u?'
  76. + '|(add|sub|mult|div|abs|neg|sqrt|c)\.[sd]$', \
  77. self.name)
  78. def is_monop(self):
  79. """Check if the statement is an unary operation."""
  80. return len(self) == 2 and self.is_arith()
  81. def is_binop(self):
  82. """Check if the statement is an binary operation."""
  83. return self.is_command() and len(self) == 3 and not self.is_jump()
  84. def is_load_non_immediate(self):
  85. """Check if the statement is a load statement."""
  86. return self.is_command() \
  87. and re.match('^l(w|a|b|bu|\.d|\.s)|dlw$', \
  88. self.name)
  89. def is_logical(self):
  90. """Check if the statement is a logical operator."""
  91. return self.is_command() and re.match('^(xor|or|and)i?$', self.name)
  92. def is_double_arithmetic(self):
  93. """Check if the statement is a arithmetic .d operator."""
  94. return self.is_command() and \
  95. re.match('^(add|sub|div|mul)\.[sd]$', self.name)
  96. def is_double_unary(self):
  97. """Check if the statement is a unary .d operator."""
  98. return self.is_command() and \
  99. re.match('^(abs|neg|mov)\.d$', self.name)
  100. def is_move_from_spec(self):
  101. """Check if the statement is a move from the result register."""
  102. return self.is_command() and self.name in ['mflo', 'mthi']
  103. def is_set_if_less(self):
  104. """Check if the statement is a shift if less then."""
  105. return self.is_command() and self.name in ['slt', 'sltu']
  106. def is_convert(self):
  107. """Check if the statement is a convert operator."""
  108. return self.is_command() and re.match('^cvt\.[a-z\.]*$', self.name)
  109. def is_truncate(self):
  110. """Check if the statement is a convert operator."""
  111. return self.is_command() and re.match('^trunc\.[a-z\.]*$', self.name)
  112. def is_compare(self):
  113. """Check if the statement is a comparison."""
  114. return self.is_command() and re.match('^c\.[a-z\.]*$', self.name)
  115. def jump_target(self):
  116. """Get the jump target of this statement."""
  117. if not self.is_jump():
  118. raise Exception('Command "%s" has no jump target' % self.name)
  119. return self[-1]
  120. def get_def(self):
  121. """Get the variable that this statement defines, if any."""
  122. instr = ['div', 'move', 'addu', 'subu', 'li', 'dmfc1', 'mov.d']
  123. defined = set()
  124. if self.is_command('mtc1'):
  125. defined.add(self[1])
  126. if self.is_load_non_immediate() or self.is_arith() \
  127. or self.is_logical() or self.is_double_arithmetic() \
  128. or self.is_move_from_spec() or self.is_double_unary() \
  129. or self.is_set_if_less() or self.is_convert() \
  130. or self.is_truncate() or self.is_load() \
  131. or self.is_command(*instr):
  132. defined.add(self[0])
  133. return defined
  134. def get_use(self, as_items=False):
  135. """Get the variables that this statement uses, if any."""
  136. instr = ['addu', 'subu', 'mult', 'div', 'move', 'mov.d', \
  137. 'dmfc1', 'div.s']
  138. use = set()
  139. indices = []
  140. # Jump to register addres uses register
  141. if self.is_command('j') and re.match('^\$\d+$', self[0]):
  142. use.add(self[0])
  143. indices.append(0)
  144. # Case arg0
  145. if (self.is_branch() \
  146. and not self.is_command('bc1f', 'bc1t', 'bct', 'bcf')) \
  147. or self.is_store() or self.is_compare() \
  148. or self.is_command('mult', 'dsz', 'mtc1'):
  149. if self.name == 'dsz':
  150. m = re.match('^[^(]+\(([^)]+)\)$', self[0])
  151. if m:
  152. use.add(m.group(1))
  153. indices.append(0)
  154. else:
  155. use.add(self[0])
  156. indices.append(0)
  157. if (self.is_branch() and not self.is_branch_zero() \
  158. and not self.is_command('bc1f', 'bc1t', 'bct', 'bcf')) \
  159. or self.is_shift() \
  160. or self.is_double_arithmetic() or self.is_double_unary() \
  161. or self.is_logical() or self.is_convert() \
  162. or self.is_truncate() or self.is_set_if_less() \
  163. or self.is_compare() or self.is_command(*instr):
  164. # Case arg1 direct adressing
  165. use.add(self[1])
  166. indices.append(1)
  167. elif self.is_load_non_immediate() or self.is_store():
  168. # Case arg1 relative adressing
  169. m = re.match('^[^(]+\(([^)]+)\)$', self[1])
  170. if m:
  171. use.add(m.group(1))
  172. indices.append(1)
  173. elif not re.match('^\$LC\d+$', self[1]):
  174. use.add(self[1])
  175. indices.append(1)
  176. # Case arg2
  177. if (self.is_double_arithmetic() or self.is_set_if_less() \
  178. or self.is_logical() or self.is_truncate() \
  179. or self.is_command('addu', 'subu', 'div')) \
  180. and not isinstance(self[2], int):
  181. use.add(self[2])
  182. indices.append(2)
  183. return zip(indices, use) if as_items else use
  184. def defines(self, reg):
  185. """Check if this statement defines the given register."""
  186. return reg in self.get_def()
  187. def uses(self, reg):
  188. """Check if this statement uses the given register."""
  189. return reg in self.get_use()
  190. def replace_usage(self, x, y):
  191. """Replace the uses of register x by y."""
  192. for i, arg in enumerate(self):
  193. self[i] = re.sub('\\' + x + '(?!\d)', y, str(arg))
  194. class Block:
  195. bid = 1
  196. def __init__(self, statements=[], verbose=False):
  197. self.statements = statements
  198. self.pointer = 0
  199. # Assign a unique ID to each block for printing purposes
  200. self.bid = Block.bid
  201. Block.bid += 1
  202. self.verbose = verbose
  203. def __str__(self):
  204. return '<Block bid=%d statements=%d>' % (self.bid, len(self))
  205. def __repr__(self):
  206. return str(self)
  207. def __iter__(self):
  208. return iter(self.statements)
  209. def __getitem__(self, n):
  210. return self.statements[n]
  211. def __len__(self):
  212. return len(self.statements)
  213. def read(self):
  214. """Read the statement at the current pointer position and move the
  215. pointer one position to the right."""
  216. s = self.statements[self.pointer]
  217. self.pointer += 1
  218. return s
  219. def end(self):
  220. """Check if the pointer is at the end of the statement list."""
  221. return self.pointer >= len(self)
  222. def peek(self, count=1):
  223. """Read the statements until an offset from the current pointer
  224. position."""
  225. if self.end():
  226. return Statement('empty', '') if count == 1 else []
  227. return self.statements[self.pointer] if count == 1 \
  228. else self.statements[self.pointer:self.pointer + count]
  229. def replace(self, count, replacement, start=None, message=''):
  230. """Replace the given range start-(start + count) with the given
  231. statement list, and move the pointer to the first statement after the
  232. replacement."""
  233. if self.pointer == 0:
  234. raise Exception('No statement have been read yet.')
  235. if start == None:
  236. start = self.pointer - 1
  237. # Add a message in inline comments
  238. if self.verbose:
  239. if len(message):
  240. message = ' ' + message
  241. if len(replacement):
  242. replacement[0].set_message(message)
  243. for s in replacement[1:]:
  244. s.set_message('|')
  245. else:
  246. replacement = [Statement('comment', message)]
  247. elif not len(replacement):
  248. # Statement is removed, comment it
  249. replacement = [Statement('comment', str(b)) \
  250. for b in self.statements[start:start + count]]
  251. before = self.statements[:start]
  252. after = self.statements[start + count:]
  253. self.statements = before + replacement + after
  254. self.pointer = start + len(replacement)
  255. def insert(self, statement, index=None, message=''):
  256. if index == None:
  257. index = self.pointer
  258. self.statements.insert(index, statement)
  259. statement.set_message(' ' + message)
  260. def apply_filter(self, callback):
  261. """Apply a filter to the statement list. If the callback returns True,
  262. the statement will remain in the list.."""
  263. self.statements = filter(callback, self.statements)
  264. def reverse_statements(self):
  265. """Reverse the statement list and reset the pointer."""
  266. self.statements = self.statements[::-1]
  267. self.reset()
  268. def reset(self):
  269. """Reset the internal pointer."""
  270. self.pointer = 0