mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
Perform redundant jump elimination in lastpass
This is a first try at redundant jump removal (jumps that target the very next instruction). It's too basic in several ways. - The statement is replaced by a ';' instead of removed. - If the jump was the only statement in an if, when the if becomes empty, it's not folded. - Jumps that are last in the 'then' branch of if+else are not visible. This would need either to track multiple last statements, or to have some means to anticipate what the next statement is at every statement. A Control Flow Graph would help a lot. - When a label is immediately followed by a jump, all jumps to that label should target the destination of that jump if it's in scope. Added to TODO. - It misses some optimizations when not expanding WHILE and FOR into IF/JUMP. Moving everything to an earlier stage would help with some of these, especially with ';' and 'if' folding. Unconditionally expanding WHILE and FOR would also help.
This commit is contained in:
parent
574f92d08e
commit
5bfb218505
4 changed files with 83 additions and 6 deletions
|
@ -26,12 +26,34 @@ from lslcommon import nr
|
||||||
#from lslparse import warning
|
#from lslparse import warning
|
||||||
#from lslfuncopt import OptimizeFunc, OptimizeArgs, FuncOptSetup
|
#from lslfuncopt import OptimizeFunc, OptimizeArgs, FuncOptSetup
|
||||||
|
|
||||||
|
class rec:
|
||||||
|
def __init__(self, **init):
|
||||||
|
for i in init:
|
||||||
|
setattr(self, i, init[i])
|
||||||
|
|
||||||
class lastpass(object):
|
class lastpass(object):
|
||||||
|
def visible(self, scope):
|
||||||
|
return scope in self.scopeStack
|
||||||
def LastPassPreOrder(self, parent, index):
|
def LastPassPreOrder(self, parent, index):
|
||||||
node = parent[index]
|
node = parent[index]
|
||||||
nt = node.nt
|
nt = node.nt
|
||||||
child = node.ch
|
child = node.ch
|
||||||
|
|
||||||
|
if (nt != '{}' and nt != ';' and nt != 'LAMBDA' and nt != '@'
|
||||||
|
and not self.inExpr
|
||||||
|
):
|
||||||
|
# Node that generates code.
|
||||||
|
# EXPR counts as a single statement for JUMP purposes.
|
||||||
|
self.prevStmt = self.lastStmt
|
||||||
|
self.lastStmt = rec(node=node, parent=parent, index=index)
|
||||||
|
# These are all the labels that label the current statement
|
||||||
|
self.lastLabels = self.curLabels
|
||||||
|
self.curLabels = set()
|
||||||
|
|
||||||
|
if nt == 'EXPR':
|
||||||
|
self.inExpr = True
|
||||||
|
return
|
||||||
|
|
||||||
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
|
||||||
|
@ -113,6 +135,30 @@ class lastpass(object):
|
||||||
# system library function
|
# system library function
|
||||||
self.usedlibfuncs.add(node.name)
|
self.usedlibfuncs.add(node.name)
|
||||||
|
|
||||||
|
if nt == 'JUMP':
|
||||||
|
if self.lastLabels:
|
||||||
|
# This jump is labeled. The jumps that go to any of its labels
|
||||||
|
# might be changeable to the destination of this jump, if the
|
||||||
|
# scope of the destination is visible from those jumps, and the
|
||||||
|
# jump eliminated.
|
||||||
|
# TODO: We need the positions of these jumps for this to work.
|
||||||
|
# We could perhaps change 'ref' in the symbol to a list instead
|
||||||
|
# of a counter, pointing to the jumps.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if nt == '@':
|
||||||
|
self.curLabels.add(node)
|
||||||
|
if self.lastStmt.node.nt == 'JUMP':
|
||||||
|
jump = self.lastStmt.node
|
||||||
|
if jump.scope == node.scope and jump.name == node.name:
|
||||||
|
# Remove the jump
|
||||||
|
self.lastStmt.parent[self.lastStmt.index] = nr(nt=';')
|
||||||
|
assert self.symtab[node.scope][node.name]['ref'] > 0
|
||||||
|
self.symtab[node.scope][node.name]['ref'] -= 1
|
||||||
|
|
||||||
|
if nt == '{}':
|
||||||
|
self.scopeStack.append(node.scope)
|
||||||
|
|
||||||
def LastPassPostOrder(self, parent, index):
|
def LastPassPostOrder(self, parent, index):
|
||||||
node = parent[index]
|
node = parent[index]
|
||||||
nt = node.nt
|
nt = node.nt
|
||||||
|
@ -124,14 +170,13 @@ class lastpass(object):
|
||||||
# function (must be the result of optimization).
|
# function (must be the result of optimization).
|
||||||
# Insert dummy IF(1){...} statement covering the whole function
|
# Insert dummy IF(1){...} statement covering the whole function
|
||||||
# (if it returs a value, insert a return statement too).
|
# (if it returs a value, insert a return statement too).
|
||||||
scope = len(self.symtab)
|
child[0] = nr(nt='{}', t=None, scope=len(self.symtab), ch=[
|
||||||
self.symtab.append({})
|
|
||||||
child[0] = nr(nt='{}', t=None, scope=scope, ch=[
|
|
||||||
nr(nt='IF', t=None, ch=[
|
nr(nt='IF', t=None, ch=[
|
||||||
nr(nt='CONST', t='integer', value=1, SEF=True),
|
nr(nt='CONST', t='integer', value=1, SEF=True),
|
||||||
child[0]
|
child[0]
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
self.symtab.append({})
|
||||||
child = node.ch
|
child = node.ch
|
||||||
if node.t is not None:
|
if node.t is not None:
|
||||||
# Inserting a state switch in a function that returns a
|
# Inserting a state switch in a function that returns a
|
||||||
|
@ -146,6 +191,15 @@ class lastpass(object):
|
||||||
del self.BadStCh
|
del self.BadStCh
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if nt == 'EXPR':
|
||||||
|
self.inExpr = False
|
||||||
|
|
||||||
|
if nt == '{}':
|
||||||
|
self.scopeStack.pop()
|
||||||
|
|
||||||
|
if nt == 'WHILE' or nt == 'DO' or nt == 'FOR':
|
||||||
|
self.prevStmt = self.lastStmt
|
||||||
|
self.lastStmt = rec(node=node, parent=parent, index=index)
|
||||||
|
|
||||||
def RecursiveLastPass(self, parent, index):
|
def RecursiveLastPass(self, parent, index):
|
||||||
subinfo = self.subinfo
|
subinfo = self.subinfo
|
||||||
|
@ -171,6 +225,13 @@ class lastpass(object):
|
||||||
|
|
||||||
self.usedlibfuncs = set()
|
self.usedlibfuncs = set()
|
||||||
|
|
||||||
|
self.scopeStack = [0]
|
||||||
|
self.curLabels = set()
|
||||||
|
self.lastLabels = set()
|
||||||
|
self.prevStmt = None
|
||||||
|
self.lastStmt = None
|
||||||
|
self.inExpr = False
|
||||||
|
|
||||||
# self.subinfo is subtree-local info.
|
# self.subinfo is subtree-local info.
|
||||||
self.subinfo = {}
|
self.subinfo = {}
|
||||||
for idx in xrange(len(tree)):
|
for idx in xrange(len(tree)):
|
||||||
|
@ -180,4 +241,6 @@ class lastpass(object):
|
||||||
self.globalmode = False
|
self.globalmode = False
|
||||||
else:
|
else:
|
||||||
self.RecursiveLastPass(tree, idx)
|
self.RecursiveLastPass(tree, idx)
|
||||||
|
|
||||||
|
assert len(self.scopeStack) == 1 and self.scopeStack[0] == 0
|
||||||
return {'libfuncs':self.usedlibfuncs}
|
return {'libfuncs':self.usedlibfuncs}
|
||||||
|
|
|
@ -22,7 +22,7 @@ default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
jump ___lbl__00001;
|
;
|
||||||
}
|
}
|
||||||
@___lbl__00001;
|
@___lbl__00001;
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@ default
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
___ret__00002 = llGetLinkNumber();
|
___ret__00002 = llGetLinkNumber();
|
||||||
jump ___rtl__00006;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@___rtl__00006;
|
@___rtl__00006;
|
||||||
|
@ -43,7 +43,7 @@ default
|
||||||
while (___ret__00002);
|
while (___ret__00002);
|
||||||
{
|
{
|
||||||
___ret__00001 = <((float)0), ((float)0), ((float)0)>;
|
___ret__00001 = <((float)0), ((float)0), ((float)0)>;
|
||||||
jump ___rtl__00004;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
unit_tests/coverage.suite/jump-opt.lsl
Normal file
13
unit_tests/coverage.suite/jump-opt.lsl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
default{state_entry(){
|
||||||
|
|
||||||
|
if (llGetLinkNumber()) jump x1;
|
||||||
|
if (llGetLinkNumber()) jump x1; else jump x3;
|
||||||
|
|
||||||
|
llOwnerSay("ok");
|
||||||
|
@x1;
|
||||||
|
jump x2;
|
||||||
|
@x3;
|
||||||
|
llOwnerSay("blergh");
|
||||||
|
@x2;
|
||||||
|
|
||||||
|
}}
|
1
unit_tests/coverage.suite/jump-opt.skp
Normal file
1
unit_tests/coverage.suite/jump-opt.skp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This optimization is not implemented.
|
Loading…
Add table
Add a link
Reference in a new issue