| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- from src.statement import Statement as S
- from math import log
- def reg_can_be_used_in(reg, block, start, end):
- """Check if a register addres safely be used in a block section using local
- dataflow analysis."""
- # Check if the register used or defined in the block section
- for s in block[start:end]:
- if s.uses(reg) or s.defines(reg):
- return False
- # Check if the register is used inside the block after the specified
- # section, without having been re-assigned first
- for s in block[end:]:
- if s.uses(reg):
- return False
- elif s.defines(reg):
- return True
- return reg not in block.out_set
- def find_free_reg(block, start, end):
- """Find a temporary register that is free in a given list of statements."""
- for i in xrange(8, 16):
- tmp = '$%d' % i
- if reg_can_be_used_in(tmp, block, start, end):
- return tmp
- raise Exception('No temporary register is available.')
- def eliminate_common_subexpressions(block):
- """
- Common subexpression elimination:
- x = a + b -> u = a + b
- y = a + b x = u
- y = u
- The algorithm used is as follows:
- - Traverse through the statements.
- - If the statement can be possibly be eliminated, walk further collecting
- all other occurrences of the expression until one of the arguments is
- assigned in a statement, or the start of the block has been reached.
- - If one or more occurrences were changed, insert the expression with a new
- destination address before the last changed occurrence and change all
- occurrences to a move instruction from that address.
- """
- changed = False
- while not block.end():
- s = block.read()
- if s.is_arith():
- pointer = block.pointer
- occurrences = [pointer - 1]
- args = s[1:]
- # Collect similar statements
- while not block.end():
- s2 = block.read()
- if not s2.is_command():
- continue
- # Stop if one of the arguments is assigned
- if len(s2) and s2[0] in args:
- break
- # Replace a similar expression by a move instruction
- if s2.name == s.name and s2[1:] == args:
- occurrences.append(block.pointer - 1)
- if len(occurrences) > 1:
- new_reg = find_free_reg(block, occurrences[0], occurrences[-1])
- # Replace all occurrences with a move statement
- for occurrence in occurrences:
- rd = block[occurrence][0]
- block.replace(1, [S('command', 'move', rd, new_reg)], \
- start=occurrence)
- # Insert the calculation before the original with the new
- # destination address
- block.insert(S('command', s.name, *([new_reg] + args)), \
- index=occurrences[0])
- changed = True
- # Reset pointer to continue from the original statement
- block.pointer = pointer
- return changed
- def to_hex(value):
- """Create the hexadecimal string of an integer."""
- return '0x%08x' % value
- def fold_constants(block):
- """
- Constant folding:
- x = 3 + 5 -> x = 8
- y = x * 2 y = 16
- To keep track of constant values, the following assumptions are made:
- - An immediate load defines a register value:
- li $reg, XX -> register[$reg] = XX
- - Integer variable definition is of the following form:
- li $reg, XX -> constants[VAR] = XX
- sw $reg, VAR -> register[$reg] = XX
- - When a variable is used, the following happens:
- lw $reg, VAR -> register[$reg] = constants[VAR]
- """
- changed = False
- # Variable values
- constants = {}
- # Current known values in register
- register = {}
- while not block.end():
- s = block.read()
- if not s.is_command():
- continue
- if s.name == 'li':
- # Save value in register
- register[s[0]] = int(s[1], 16)
- elif s.name == 'move' and s[0] in register:
- reg_to, reg_from = s
- if reg_from in register:
- # Other value is also known, copy its value
- register[reg_to] = register[reg_from]
- else:
- # Other value is unknown, delete the value
- del register[reg_to]
- elif s.name == 'sw' and s[0] in register:
- # Constant variable definition, e.g. 'int a = 1;'
- constants[s[1]] = register[s[0]]
- elif s.name == 'lw' and s[1] in constants:
- # Usage of variable with constant value
- register[s[0]] = constants[s[1]]
- elif s.name == 'mflo':
- # Move of `Lo' register to another register
- register[s[0]] = register['$lo']
- elif s.name == 'mfhi':
- # Move of `Hi' register to another register
- register[s[0]] = register['$hi']
- elif s.name in ['mult', 'div'] \
- and s[0]in register and s[1] in register:
- # Multiplication/division with constants
- rs, rt = s
- a, b = register[rs], register[rt]
- if s.name == 'mult':
- if not a or not b:
- # Multiplication by 0
- hi = lo = to_hex(0)
- elif a == 1:
- # Multiplication by 1
- hi = to_hex(0)
- lo = to_hex(b)
- elif b == 1:
- # Multiplication by 1
- hi = to_hex(0)
- lo = to_hex(a)
- else:
- # Calculate result and fill Hi/Lo registers
- binary = bin(a * b)[2:]
- binary = '0' * (64 - len(binary)) + binary
- hi = int(binary[:32], base=2)
- lo = int(binary[32:], base=2)
- # Replace the multiplication with two immidiate loads to the
- # Hi/Lo registers
- block.replace(1, [S('command', 'li', '$hi', hi),
- S('command', 'li', '$lo', li)])
- elif s.name == 'div':
- lo, hi = divmod(rs, rt)
- register['$lo'], register['$hi'] = lo, hi
- changed = True
- elif s.name in ['addu', 'subu']:
- # Addition/subtraction with constants
- rd, rs, rt = s
- rs_known = rs in register
- rt_known = rt in register
- if rs_known and rt_known:
- # a = 5 -> b = 15
- # c = 10
- # b = a + c
- rs_val = register[rs]
- rt_val = register[rt]
- if s.name == 'addu':
- result = to_hex(rs_val + rt_val)
- if s.name == 'subu':
- result = to_hex(rs_val - rt_val)
- block.replace(1, [S('command', 'li', rd, result)])
- register[rd] = result
- changed = True
- continue
- if rt_known:
- # a = 10 -> b = c + 10
- # b = c + a
- s[2] = register[rt]
- changed = True
- elif rs_known and s.name == 'addu':
- # c = 10 -> b = a + 10
- # b = c + a
- s[1] = rt
- s[2] = register[rs]
- changed = True
- if s[2] == 0:
- # Addition/subtraction with 0
- block.replace(1, [S('command', 'move', rd, s[1])])
- else:
- for reg in s.get_def():
- if reg in register:
- # Known register is overwritten, remove its value
- del register[reg]
- return changed
- def copy_propagation(block):
- """
- Unpack a move instruction, by replacing its destination
- address with its source address in the code following the move instruction.
- This way, the move statement might be a target for dead code elimination.
- move $regA, $regB move $regA, $regB
- ... ...
- Code not writing $regA, -> ...
- $regB ...
- ... ...
- addu $regC, $regA, ... addu $regC, $regB, ...
- """
- moves_from = []
- moves_to = []
- changed = False
- while not block.end():
- s = block.read()
- if s.is_command('move') and s[0] not in moves_to:
- # Add this move to the lists, because it is not yet there.
- moves_from.append(s[1])
- moves_to.append(s[0])
- elif s.is_command('move') and s[0] in moves_to:
- # This move is already in the lists, so only update it
- for i in xrange(len(moves_to)):
- if moves_to[i] == s[0]:
- moves_from[i] = s[1]
- continue
- elif (len(s) == 3 or s.is_command('mlfo') or s.is_load()) \
- and (s[0] in moves_to or s[0] in moves_from):
- # One of the registers gets overwritten, so remove the data from
- # the list.
- i = 0
- while i < len(moves_to):
- if moves_to[i] == s[0] or moves_to[i] == s[1]:
- del moves_to[i]
- del moves_from[i]
- else:
- i += 1
- elif len(s) == 3 and (s[1] in moves_to or s[2] in moves_to):
- # Check where the result of the move is used and replace it with
- # the original variable.
- for i in xrange(len(moves_to)):
- if s[1] == moves_to[i]:
- s[1] = moves_from[i]
- continue
- if s[2] == moves_to[i]:
- s[2] = moves_from[i]
- continue
- changed = True
- return changed
- def algebraic_transformations(block):
- """
- Change ineffective or useless algebraic expressions. Handled are:
- - x = y + 0 -> x = y
- - x = y - 0 -> x = y
- - x = y * 1 -> x = y
- - x = y * 0 -> x = 0
- - x = y * 2 -> x = x << 1
- """
- changed = False
- while not block.end():
- s = block.read()
- if (s.is_command('addu') or s.is_command('subu')) and s[2] == 0:
- block.replace(1, [S('command', 'move', s[0], s[1])])
- changed = True
- elif s.is_command('mult'):
- mflo = block.peek()
- if mflo.is_command('mflo'):
- if s[1] == 1:
- block.replace(2, [S('command', 'move', mflo[0], s[0])])
- changed = True
- continue
- elif s[1] == 0:
- block.replace(2, [S('command', 'li', '$1', to_hex(0))])
- changed = True
- continue
- shift_amount = log(s[1], 2)
- if shift_amount.is_integer():
- new_command = S('command', 'sll', \
- mflo[0], s[0], \
- int(shift_amount))
- block.replace(2, [new_command])
- changed = True
- return changed
- def eliminate_dead_code(block):
- """
- Dead code elimination:
- TODO: example...
- The algorithm used is as follows:
- - Traverse through the statements in reverse order.
- - If the statement definition is dead, remove it. A variable is dead if it
- is not used in the rest of the block, and is not in the `out' set of the
- block.
- """
- # TODO: Finish
- changed = False
- unused = set()
- for s in reversed(block):
- for reg in s.get_def():
- if reg in unused:
- # Statement is redefined later, so this statement is useless
- s.remove = True
- #print 'reg %s is in %s, remove:' % (reg, unused), \
- # block.pointer - 1, s
- else:
- unused.add(reg)
- unused -= set(s.get_use())
- block.apply_filter(lambda s: not hasattr(s, 'remove'))
- return changed
|