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:
Sei Lisa 2017-11-02 16:39:12 +01:00
parent e4eaab9e84
commit a87022b73f
4 changed files with 77 additions and 29 deletions

View file

@ -67,6 +67,10 @@ LSLType2Python = {'integer':int, 'float':float,
'string':unicode, 'key':Key, 'vector':Vector,
'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):
assert type(txt) == unicode
sys.stderr.write(u"WARNING: " + txt + u"\n")

View file

@ -1543,8 +1543,6 @@ class foldconst(object):
or child[idx]['nt'] == '{}' and not child[idx]['ch']:
del child[idx]
else:
if 'StSw' in child[idx]:
node['StSw'] = True
idx += 1
if issef:
node['SEF'] = True
@ -1558,19 +1556,6 @@ class foldconst(object):
# We might be able to remove one of the branches.
if lslfuncs.cond(child[0]['value']):
self.FoldTree(child, 1)
# If it has a state switch, the if() must be preserved
# (but the else branch may be removed).
if 'StSw' in child[1]:
# TODO: Get rid of StSw craziness and make another pass
# to put them under conditionals if present (if bald
# 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
@ -1788,7 +1773,6 @@ class foldconst(object):
if nt == 'STSW':
# State switch always has side effects.
node['StSw'] = True
return
if nt == 'SUBIDX':

View file

@ -31,6 +31,33 @@ class lastpass(object):
nt = node['nt']
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
and (nt == 'CONST' and node['t'] == 'list' or nt == 'LIST'
or nt == '+' and child[0]['t'] == 'list' and
@ -61,10 +88,9 @@ class lastpass(object):
'SEF':True,
'value':elem
} if listnode['nt'] == 'CONST' else elem
left = self.Cast(elemnode, 'list') if left is None else {
'nt':'+', 't':'list', 'SEF':True,
'ch':[left, elemnode]
}
left = (self.Cast(elemnode, 'list') if left is None
else {'nt':'+', 't':'list', 'SEF':True,
'ch':[left, elemnode]})
del elemnode
if left is not None: # it's none for empty lists
parent[index] = left
@ -75,9 +101,40 @@ class lastpass(object):
del listnode, left
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):
subinfo = self.subinfo
self.subinfo = subinfo.copy()
self.LastPassPreOrder(parent, index)
if 'ch' in parent[index]:
@ -88,6 +145,7 @@ class lastpass(object):
idx += 1
self.LastPassPostOrder(parent, index)
self.subinfo = subinfo
def LastPass(self):
self.globalmode = False
@ -95,6 +153,8 @@ class lastpass(object):
tree = self.tree
# Last optimizations pass
# self.subinfo is subtree-local info.
self.subinfo = {}
for idx in xrange(len(tree)):
if tree[idx]['nt'] == 'DECL':
self.globalmode = True

View file

@ -272,7 +272,7 @@ class Test03_Optimizer(UnitTestCase):
vector vvvv2=vvvv;
float ffff3 = v.z;
integer fn(){
if (1) state default; else return 2;
if (1) state default;
return fn();}
default{touch(integer n){