First prototype version of the optimizer.

This commit is contained in:
Sei Lisa 2014-07-27 02:19:54 +02:00
parent 603b85afc8
commit 59de1e1f4f

View file

@ -11,93 +11,279 @@ class optimizer(object):
binary_ops = frozenset(('+','-','*','/','%','<<','>>','<','<=','>','>=',
'==','!=','|','^','&','||','&&'))
def FoldStmt(self, code):
"""If the statement is a constant or an identifier, remove it as it does
nothing.
"""
if code[0] in (CONSTANT, 'IDENT', 'FIELD'):
code[:] = [S[';'], None]
else:
code[:] = code
def FoldTree(self, code):
"""Recursively traverse the tree to fold constants, changing the tree
in place.
"""Recursively traverse the tree to fold constants, changing it in
place.
Also optimizes away IF, WHILE, etc.
"""
while code[0] == 'EXPR':
code[:] = code[2]
if code[0] == 'CAST':
code0 = code[0]
if code0 == 'CAST':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
if code[1] != 'key': # key constants not possible
# Enable key constants. We'll typecast them back on output, but
# this enables some optimizations.
#if code[1] != 'key': # key constants not possible
code[:] = [CONSTANT, code[1], lslfuncs.typecast(code[2][2])]
return
if code[0] == '-':
self.FoldTree(code[3])
if code[3][1] in ('integer', 'float'): # no gain otherwise
if code[3][0] == CONSTANT:
code[3][2] = -code[3][2]
else:
code[:] = ['+', code[1], code[2], [S['NEG'], code[3][1], code[3]]]
if code0 == 'NEG':
self.FoldTree(code[2])
else:
self.FoldTree(code[2])
if code[2][0] == code[3][0] == CONSTANT:
code[:] = ['-', code[1], lslfuncs.sub(code[2][2], code[3][2])]
# Fall through to optimize it right away as addition
if code[2][0] == CONSTANT:
code[:] = [CONSTANT, code[1], lslfuncs.neg(code[2][2])]
return
if code[0] in self.binary_ops:
if code0 == '!':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
code[:] = [CONSTANT, code[1], int(not code[2][2])]
return
if code0 == '~':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
code[:] = [CONSTANT, code[1], ~code[2][2]]
return
if code0 == '()':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
code[:] = code[2]
if code0 in self.binary_ops:
# RTL evaluation
self.FoldTree(code[3])
self.FoldTree(code[2])
if code[2][0] == code[3][0] == CONSTANT:
code[:] = [CONSTANT, code[1], lslfuncs.add(code[2][2], code[3][2])]
op = code0
op1 = code[2][2]
op2 = code[3][2]
if op == '+':
result = lslfuncs.add(op1, op2)
elif op == '-':
result = lslfuncs.sub(op1, op2)
elif op == '*':
result = lslfuncs.mul(op1, op2)
elif op == '/':
result = lslfuncs.div(op1, op2)
elif op == '%':
result = lslfuncs.mod(op1, op2)
elif op == '<<':
result = lslfuncs.S32(op1 << (op2 & 31))
elif op == '>>':
result = lslfuncs.S32(op1 >> (op2 & 31))
elif op == '==' or op == '!=':
result = lslfuncs.compare(op1, op2, op == '==')
elif op in ('<', '<=', '>', '>='):
if op in ('>', '<='):
result = lslfuncs.less(op2, op1)
else:
result = lslfuncs.less(op1, op2)
if op in ('>=', '<='):
result = not result
elif op == '|':
result = op1 | op2
elif op == '^':
result = op1 ^ op2
elif op == '&':
result = op1 & op2
elif op == '||':
result = int(op1 or op2)
elif op == '&&':
result = int(op1 and op2)
else:
raise Exception(u'Internal error: Operator not found: ' + op.decode('utf8'))
code[:] = [CONSTANT, code[1], result]
elif code[0] == '-' and code[2][1] in ('integer', 'float') and code[3][1] in ('integer', 'float'):
# Change - to + - for int/float
if code[3][0] == CONSTANT:
code[3][2] = lslfuncs.neg(code[3][2])
else:
code[:] = [S['+'], code[1], code[2], [S['NEG'], code[3][1], code[3]]]
elif code[0] == '<<' and code[3][0] == CONSTANT:
# Transforming << into multiply saves some bytes.
if code[2][0] in ('+', '-', 'NEG'): # operands with priority between * and <<
code[2] = [S['()'], code[2][1], code[2]]
code[:] = [S['*'], code[1], code[2], 1<<(code[3][2] & 31)]
return
if self.globalmode:
if code[0] == 'IDENT':
if code[1] != 'key' and self.symtab[code[2]][2] is not None:
if code0 == 'IDENT':
if code[1] != 'key' and self.symtab[code[3]][code[2]][2] is not None:
code[:] = [CONSTANT, code[1], self.symtab[code[2]][2]]
return
if code[0] == 'FUNCTION':
if code0 == 'FUNCTION':
for x in code[3][::-1]:
self.FoldTree(x)
if code[2] in self.functions:
if code[2] in self.functions and self.functions[code[2]][2] is not None:
for x in code[3]:
if x[0] != CONSTANT:
break
else:
if code[2] in self.functions:
# Call it
val = self.functions[code[2]](tuple(x[2] for x in code[3]))
val = self.functions[code[2]][2](*tuple(x[2] for x in code[3]))
code[:] = [CONSTANT, code[1], val]
return
if code[0] == 'PRINT':
if code0 == 'PRINT':
# useless but who knows
self.FoldTree(code[2])
if code[0] == '{}':
for x in code[2:]:
self.FoldTree(x)
return
if code[0] in ('VECTOR', 'ROTATION', 'LIST'):
if code0 in ('VECTOR', 'ROTATION', 'LIST'):
for x in code[:1:-1]:
self.FoldTree(x)
if code[0] == 'FIELD':
self.FoldTree(code[2])
assert code[2][0] in ('VECTOR', 'ROTATION')
idx = '--xyzs'.index(code[3])
if code[2][idx][0] == CONSTANT:
code[:] = [CONSTANT, 'float', code[2][idx][0]]
# TODO: Fold into constant if possible.
return
def Fold(self, code, IsGlobal = False):
if code0 == 'FIELD':
# FIXME: I was drunk when I wrote this.
#self.FoldTree(code[2])
#assert code[2][1] in ('vector', 'rotation')
#idx = '--xyzs'.index(code[3])
#if code[2][idx][0] == CONSTANT:
# code[:] = [CONSTANT, 'float', code[2][idx][0]]
if self.globalmode:
# We can fold a vector or rotation field as they are constant.
assert code[2][0] == 'IDENT'
value = self.symtab[code[2][3]][code[2][2]][2]
assert type(value) in (lslfuncs.Vector, lslfuncs.Quaternion)
code[:] = [CONSTANT, 'float', lslfuncs.ff(value['xyzs'].index(code[3]))]
return
if code0 == '{}':
for x in code[2:]:
self.FoldTree(x)
self.FoldStmt(x)
return
if code0 == 'IF':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
# We can remove one of the branches safely.
if lslfuncs.cond(code[2][2]):
self.FoldTree(code[3])
code[:] = code[3]
self.FoldStmt(code)
elif len(code) > 4:
self.FoldTree(code[4])
code[:] = code[4]
self.FoldStmt(code)
else:
# No ELSE branch, replace the statement with an empty one.
code[:] = [S[';'], None]
else:
self.FoldTree(code[3])
self.FoldStmt(code[3])
if len(code) > 4:
self.FoldTree(code[4])
self.FoldStmt(code[4])
return
if code0 == 'WHILE':
self.FoldTree(code[2])
if code[2][0] == CONSTANT:
# See if the whole WHILE can be eliminated.
if lslfuncs.cond(code[2][2]):
# Endless loop which must be kept.
# First, replace the constant.
code[2][1:2] = [S['integer'], 1]
# Recurse on the statement.
self.FoldTree(code[3])
self.FoldStmt(code[3])
else:
# Can be removed.
code[:] = [S[';'], None]
else:
self.FoldTree(code[3])
self.FoldStmt(code[3])
return
if code0 == 'DO':
self.FoldTree(code[2]) # This one is always executed.
self.FoldStmt(code[2])
self.FoldTree(code[3])
# See if the latest part is a constant.
if code[3][0] == CONSTANT:
if lslfuncs.cond(code[3][2]):
# Endless loop. Replace the constant.
code[3][1:2] = [S['integer'], 1]
else:
# Only one go. Replace with the statement(s).
code[:] = code[2]
return
if code0 == 'FOR':
for x in code[2]:
self.FoldTree(x) # Initializer expresion list, always executed.
self.FoldTree(code[3]) # Condition.
if code[3][0] == CONSTANT:
# FOR is delicate. It can have multiple expressions at start.
# And if there is more than one, these expressions will need a
# new block, which means new scope, which is dangerous.
# They are expressions, no declarations or labels allowed, but
# it feels creepy.
if lslfuncs.cond(code[3][2]):
# Endless loop. Just replace the constant and traverse the rest.
code[3][1:2] = [S['integer'], 1]
for x in code[4]:
self.FoldTree(x)
self.FoldTree(5)
self.FoldStmt(5)
elif len(code[2]) > 1:
code[:] = [S['{}'], None, code[2]]
elif code[2]:
code[:] = code[2][0]
else:
code[:] = [S[';'], None]
else:
for x in code[4]:
self.FoldTree(x)
self.FoldTree(code[5])
self.FoldStmt(code[5])
return
if code0 == 'RETURN':
if code[2] is not None:
self.FoldTree(code[2])
if code0 == 'DECL':
# The expression code is elsewhere.
expr = self.symtab[code[3]][code[2]][2]
if expr is not None:
self.FoldTree(expr)
def Fold(self, code, IsGlobal = True):
assert type(code[2]) == tuple
tree = list(code[2])
self.globalmode = IsGlobal
self.globalmode = IsGlobal and len(code) == 3
self.FoldTree(tree)
# As a special case, we fold the constants that are keys,
# because the folder
# TODO: Move this to a post-folding optimization.
# Reasons: (1) it doesn't optimize deep constants and
# (2) it disturbs normal folding if done on the fly.
# Mono optimization: (integer)-5 and (float)-3.0 is cheaper.
if not IsGlobal and tree[0] == 'CONSTANT':
if tree[1] == 'integer' and tree[2] < 0:
tree[:] = [S['CAST'], 'integer', tree]
elif tree[1] == 'float' and tree[2] < 0.0 and not math.isinf(tree[2]):
# Disabled because we print integer constants in hex anyway.
#if tree[1] == 'integer' and tree[2] < 0:
# tree[:] = [S['CAST'], 'integer', tree]
if tree[1] == 'float' and tree[2] < 0.0 and not math.isinf(tree[2]):
tree[:] = [S['CAST'], 'float', tree]
if type(code) == tuple:
@ -108,6 +294,8 @@ class optimizer(object):
assert False
code[2] = tuple(tree)
del self.globalmode
def optimize(self, symtab, functions):
"""Optimize the symbolic table symtab in place. Uses a table of
predefined functions for folding constants.
@ -122,6 +310,6 @@ class optimizer(object):
entry = symtab[0][name]
if entry[1] == 'State':
for event in entry[2]:
self.Fold(entry[2][event])
self.Fold(entry[2][event], False)
elif type(entry[2]) == tuple:
self.Fold(entry, IsGlobal = True) # global
self.Fold(entry) # global