mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
Get rid of StSw craziness and use lsllastpass to make another pass.
This has been a TODO item for long. Now that we have lsllastpass, it's actually easy to implement. Adds an LSLTypeDefaults dictionary to lslcommon, just in case the state-changing function returns a value and we need to insert a return statement. We've also added subtree-local info to lsllastpass (lost when we return to the parent after visiting a subtree). This fixes a bug where naked switch statements could appear as a result of optimization, and cause the compilation to fail.
This commit is contained in:
parent
e4eaab9e84
commit
a87022b73f
4 changed files with 77 additions and 29 deletions
|
@ -67,6 +67,10 @@ LSLType2Python = {'integer':int, 'float':float,
|
||||||
'string':unicode, 'key':Key, 'vector':Vector,
|
'string':unicode, 'key':Key, 'vector':Vector,
|
||||||
'rotation':Quaternion, 'list':list}
|
'rotation':Quaternion, 'list':list}
|
||||||
|
|
||||||
|
LSLTypeDefaults = {'integer':0, 'float':0.0, 'string':u'', 'key':Key(u''),
|
||||||
|
'vector':Vector((0.,0.,0.)), 'rotation':Quaternion((0.,0.,0.,1.)),
|
||||||
|
'list':[]}
|
||||||
|
|
||||||
def warning(txt):
|
def warning(txt):
|
||||||
assert type(txt) == unicode
|
assert type(txt) == unicode
|
||||||
sys.stderr.write(u"WARNING: " + txt + u"\n")
|
sys.stderr.write(u"WARNING: " + txt + u"\n")
|
||||||
|
|
|
@ -1543,8 +1543,6 @@ class foldconst(object):
|
||||||
or child[idx]['nt'] == '{}' and not child[idx]['ch']:
|
or child[idx]['nt'] == '{}' and not child[idx]['ch']:
|
||||||
del child[idx]
|
del child[idx]
|
||||||
else:
|
else:
|
||||||
if 'StSw' in child[idx]:
|
|
||||||
node['StSw'] = True
|
|
||||||
idx += 1
|
idx += 1
|
||||||
if issef:
|
if issef:
|
||||||
node['SEF'] = True
|
node['SEF'] = True
|
||||||
|
@ -1558,27 +1556,14 @@ class foldconst(object):
|
||||||
# We might be able to remove one of the branches.
|
# We might be able to remove one of the branches.
|
||||||
if lslfuncs.cond(child[0]['value']):
|
if lslfuncs.cond(child[0]['value']):
|
||||||
self.FoldTree(child, 1)
|
self.FoldTree(child, 1)
|
||||||
# If it has a state switch, the if() must be preserved
|
self.FoldStmt(child, 1)
|
||||||
# (but the else branch may be removed).
|
if len(child) == 3 and child[2]['nt'] == '@':
|
||||||
if 'StSw' in child[1]:
|
# Corner case. The label is in the same scope as
|
||||||
# TODO: Get rid of StSw craziness and make another pass
|
# this statement, so it must be preserved just in
|
||||||
# to put them under conditionals if present (if bald
|
# case it's jumped to.
|
||||||
# state switches are present, it means they are the
|
|
||||||
# result of optimization so they must be wrapped in an
|
|
||||||
# IF statement). The current approach leaves unnecessary
|
|
||||||
# IFs behind.
|
|
||||||
if len(child) == 3 and child[2]['nt'] != '@':
|
|
||||||
del child[2] # Delete ELSE if present
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
self.FoldStmt(child, 1)
|
|
||||||
if len(child) == 3 and child[2]['nt'] == '@':
|
|
||||||
# Corner case. The label is in the same scope as
|
|
||||||
# this statement, so it must be preserved just in
|
|
||||||
# case it's jumped to.
|
|
||||||
return
|
|
||||||
parent[index] = child[1]
|
|
||||||
return
|
return
|
||||||
|
parent[index] = child[1]
|
||||||
|
return
|
||||||
elif len(child) == 3:
|
elif len(child) == 3:
|
||||||
self.FoldTree(child, 2)
|
self.FoldTree(child, 2)
|
||||||
self.FoldStmt(child, 2)
|
self.FoldStmt(child, 2)
|
||||||
|
@ -1788,7 +1773,6 @@ class foldconst(object):
|
||||||
|
|
||||||
if nt == 'STSW':
|
if nt == 'STSW':
|
||||||
# State switch always has side effects.
|
# State switch always has side effects.
|
||||||
node['StSw'] = True
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if nt == 'SUBIDX':
|
if nt == 'SUBIDX':
|
||||||
|
|
|
@ -31,6 +31,33 @@ class lastpass(object):
|
||||||
nt = node['nt']
|
nt = node['nt']
|
||||||
child = node['ch'] if 'ch' in node else None
|
child = node['ch'] if 'ch' in node else None
|
||||||
|
|
||||||
|
if nt == 'FNDEF':
|
||||||
|
# StChAreBad will be True if this is a user-defined function,
|
||||||
|
# where state changes are considered bad.
|
||||||
|
# BadStCh will be True if at least one state change statement
|
||||||
|
# is found while monitoring state changes.
|
||||||
|
self.subinfo['StChAreBad'] = 'scope' in node
|
||||||
|
self.BadStCh = False
|
||||||
|
|
||||||
|
if nt == 'IF':
|
||||||
|
if len(child) == 2:
|
||||||
|
# Don't monitor the children.
|
||||||
|
self.subinfo['StChAreBad'] = False
|
||||||
|
|
||||||
|
if nt == 'DO':
|
||||||
|
self.subinfo['StChAreBad'] = False
|
||||||
|
|
||||||
|
if nt == 'FOR':
|
||||||
|
self.subinfo['StChAreBad'] = False
|
||||||
|
|
||||||
|
if nt == 'WHILE':
|
||||||
|
self.subinfo['StChAreBad'] = False
|
||||||
|
|
||||||
|
if nt == 'STSW':
|
||||||
|
if self.subinfo['StChAreBad']:
|
||||||
|
# Found one.
|
||||||
|
self.BadStCh = True
|
||||||
|
|
||||||
if (self.optlistadd and not self.globalmode
|
if (self.optlistadd and not self.globalmode
|
||||||
and (nt == 'CONST' and node['t'] == 'list' or nt == 'LIST'
|
and (nt == 'CONST' and node['t'] == 'list' or nt == 'LIST'
|
||||||
or nt == '+' and child[0]['t'] == 'list' and
|
or nt == '+' and child[0]['t'] == 'list' and
|
||||||
|
@ -61,10 +88,9 @@ class lastpass(object):
|
||||||
'SEF':True,
|
'SEF':True,
|
||||||
'value':elem
|
'value':elem
|
||||||
} if listnode['nt'] == 'CONST' else elem
|
} if listnode['nt'] == 'CONST' else elem
|
||||||
left = self.Cast(elemnode, 'list') if left is None else {
|
left = (self.Cast(elemnode, 'list') if left is None
|
||||||
'nt':'+', 't':'list', 'SEF':True,
|
else {'nt':'+', 't':'list', 'SEF':True,
|
||||||
'ch':[left, elemnode]
|
'ch':[left, elemnode]})
|
||||||
}
|
|
||||||
del elemnode
|
del elemnode
|
||||||
if left is not None: # it's none for empty lists
|
if left is not None: # it's none for empty lists
|
||||||
parent[index] = left
|
parent[index] = left
|
||||||
|
@ -75,9 +101,40 @@ class lastpass(object):
|
||||||
del listnode, left
|
del listnode, left
|
||||||
|
|
||||||
def LastPassPostOrder(self, parent, index):
|
def LastPassPostOrder(self, parent, index):
|
||||||
pass
|
node = parent[index]
|
||||||
|
nt = node['nt']
|
||||||
|
child = node['ch'] if 'ch' in node else None
|
||||||
|
|
||||||
|
if nt == 'FNDEF':
|
||||||
|
if 'scope' in node and self.BadStCh:
|
||||||
|
# There is at least one bad state change statement in the
|
||||||
|
# function (must be the result of optimization).
|
||||||
|
# Insert dummy IF(1){...} statement covering the whole function
|
||||||
|
# (if it returs a value, insert a return statement too).
|
||||||
|
child[0] = {'nt':'{}', 't':None, 'ch':[
|
||||||
|
{'nt':'IF', 't':None, 'ch':[
|
||||||
|
{'nt':'CONST', 't':'integer', 'value':1},
|
||||||
|
child[0]
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
child = node['ch']
|
||||||
|
if node['t'] is not None:
|
||||||
|
# Inserting a state switch in a function that returns a
|
||||||
|
# value must count as one of the dumbest things to do.
|
||||||
|
# We do as best as we can: add a return statement with the
|
||||||
|
# default value for the type.
|
||||||
|
child[0]['ch'].append({'nt':'RETURN', 't':None, 'ch':[
|
||||||
|
{'nt':'CONST', 't':node['t'],
|
||||||
|
'value':lslcommon.LSLTypeDefaults[node['t']]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
del self.BadStCh
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def RecursiveLastPass(self, parent, index):
|
def RecursiveLastPass(self, parent, index):
|
||||||
|
subinfo = self.subinfo
|
||||||
|
self.subinfo = subinfo.copy()
|
||||||
self.LastPassPreOrder(parent, index)
|
self.LastPassPreOrder(parent, index)
|
||||||
|
|
||||||
if 'ch' in parent[index]:
|
if 'ch' in parent[index]:
|
||||||
|
@ -88,6 +145,7 @@ class lastpass(object):
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
self.LastPassPostOrder(parent, index)
|
self.LastPassPostOrder(parent, index)
|
||||||
|
self.subinfo = subinfo
|
||||||
|
|
||||||
def LastPass(self):
|
def LastPass(self):
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
|
@ -95,6 +153,8 @@ class lastpass(object):
|
||||||
tree = self.tree
|
tree = self.tree
|
||||||
|
|
||||||
# Last optimizations pass
|
# Last optimizations pass
|
||||||
|
# self.subinfo is subtree-local info.
|
||||||
|
self.subinfo = {}
|
||||||
for idx in xrange(len(tree)):
|
for idx in xrange(len(tree)):
|
||||||
if tree[idx]['nt'] == 'DECL':
|
if tree[idx]['nt'] == 'DECL':
|
||||||
self.globalmode = True
|
self.globalmode = True
|
||||||
|
|
|
@ -272,7 +272,7 @@ class Test03_Optimizer(UnitTestCase):
|
||||||
vector vvvv2=vvvv;
|
vector vvvv2=vvvv;
|
||||||
float ffff3 = v.z;
|
float ffff3 = v.z;
|
||||||
integer fn(){
|
integer fn(){
|
||||||
if (1) state default; else return 2;
|
if (1) state default;
|
||||||
return fn();}
|
return fn();}
|
||||||
|
|
||||||
default{touch(integer n){
|
default{touch(integer n){
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue