From 5bfb218505437b96faab028276cd74ca0a754348 Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Sun, 6 Jan 2019 22:32:19 +0100 Subject: [PATCH] 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. --- lslopt/lsllastpass.py | 69 ++++++++++++++++++++++++-- unit_tests/coverage.suite/inline-4.out | 6 +-- unit_tests/coverage.suite/jump-opt.lsl | 13 +++++ unit_tests/coverage.suite/jump-opt.skp | 1 + 4 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 unit_tests/coverage.suite/jump-opt.lsl create mode 100644 unit_tests/coverage.suite/jump-opt.skp diff --git a/lslopt/lsllastpass.py b/lslopt/lsllastpass.py index d1b95f1..5d541b4 100644 --- a/lslopt/lsllastpass.py +++ b/lslopt/lsllastpass.py @@ -26,12 +26,34 @@ from lslcommon import nr #from lslparse import warning #from lslfuncopt import OptimizeFunc, OptimizeArgs, FuncOptSetup +class rec: + def __init__(self, **init): + for i in init: + setattr(self, i, init[i]) + class lastpass(object): + def visible(self, scope): + return scope in self.scopeStack def LastPassPreOrder(self, parent, index): node = parent[index] nt = node.nt 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 and (nt == 'CONST' and node.t == 'list' or nt == 'LIST' or nt == '+' and child[0].t == 'list' and @@ -113,6 +135,30 @@ class lastpass(object): # system library function 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): node = parent[index] nt = node.nt @@ -124,14 +170,13 @@ class lastpass(object): # 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). - scope = len(self.symtab) - self.symtab.append({}) - child[0] = nr(nt='{}', t=None, scope=scope, ch=[ + child[0] = nr(nt='{}', t=None, scope=len(self.symtab), ch=[ nr(nt='IF', t=None, ch=[ nr(nt='CONST', t='integer', value=1, SEF=True), child[0] ]) ]) + self.symtab.append({}) child = node.ch if node.t is not None: # Inserting a state switch in a function that returns a @@ -146,6 +191,15 @@ class lastpass(object): del self.BadStCh 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): subinfo = self.subinfo @@ -171,6 +225,13 @@ class lastpass(object): 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 = {} for idx in xrange(len(tree)): @@ -180,4 +241,6 @@ class lastpass(object): self.globalmode = False else: self.RecursiveLastPass(tree, idx) + + assert len(self.scopeStack) == 1 and self.scopeStack[0] == 0 return {'libfuncs':self.usedlibfuncs} diff --git a/unit_tests/coverage.suite/inline-4.out b/unit_tests/coverage.suite/inline-4.out index 37cf823..52d485c 100644 --- a/unit_tests/coverage.suite/inline-4.out +++ b/unit_tests/coverage.suite/inline-4.out @@ -22,7 +22,7 @@ default } } { - jump ___lbl__00001; + ; } @___lbl__00001; { @@ -35,7 +35,7 @@ default { { ___ret__00002 = llGetLinkNumber(); - jump ___rtl__00006; + ; } } @___rtl__00006; @@ -43,7 +43,7 @@ default while (___ret__00002); { ___ret__00001 = <((float)0), ((float)0), ((float)0)>; - jump ___rtl__00004; + ; } } } diff --git a/unit_tests/coverage.suite/jump-opt.lsl b/unit_tests/coverage.suite/jump-opt.lsl new file mode 100644 index 0000000..b833323 --- /dev/null +++ b/unit_tests/coverage.suite/jump-opt.lsl @@ -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; + +}} \ No newline at end of file diff --git a/unit_tests/coverage.suite/jump-opt.skp b/unit_tests/coverage.suite/jump-opt.skp new file mode 100644 index 0000000..9a25ba9 --- /dev/null +++ b/unit_tests/coverage.suite/jump-opt.skp @@ -0,0 +1 @@ +This optimization is not implemented.