From 3669bbb06e9139ee686edd76dc3b36207197c459 Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Sat, 7 May 2016 04:37:58 +0200 Subject: [PATCH] Only add a label and a jump for default and break when necessary. Well, within reasonable limits. For break, it's explained in the code. If the block is empty of actual code (code that generates output), then the break position is eliminated. For default, if the default label is at the top of the block, the jump and the label are both eliminated. This check doesn't include verifying that there are other non-code-generating statements in between, so there's room for improvement (added corresponding TODO item). This way, we're on par with FS, in case someone (ab)used the FS brokeness when the 'default' label was absent. All one has to do now is add the default label at the top, and the generated code will be the same as it was in FS when abusing that bug, with no extra burden. Example: In FS, the code at the top acted as default and there was no jump for it: default { timer() { switch(1) { 0; case 1: 1; } }} This generated roughly: default { timer() { { if (1 == 1) jump CASE_1; 0; @CASE_1; 1; } }} In this system, it would trigger an error by default. To obtain the FS behaviour, the scripter can do instead: default { timer() { switch(1) { default: 0; case 1: 1; } }} Thanks to this optimization, the code will be the same as in FS. Without it, an extra default label and corresponding jump would be present. --- lslopt/lslparse.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index 7d7edcf..1636ada 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -545,6 +545,19 @@ class parser(object): raise EParseUEOF(self) raise EParseSyntax(self) + def does_something(self, blk): + """Tell if a list of nodes does something or is just empty statements + (a pure combination of ';' and '{}' and '@') + """ + for node in blk: + if '@' != node['nt'] != ';': + if node['nt'] == '{}': + if self.does_something(node['ch']): + return True + else: + return True + return False + def Parse_vector_rotation_tail(self): """(See Parse_unary_postfix_expression for context) @@ -1682,13 +1695,25 @@ list lazy_list_set(list L, integer i, list v) if switchcasedefault is None: if self.errmissingdefault: raise EParseMissingDefault(self) - switchcasedefault = brk - self.breakstack[-1][2] = True - # TODO: Check if this JUMP is necessary - # (it won't be if the default label is the next instruction) - # This is arguably better done after DCR. - prelude.append({'nt':'JUMP', 't':None, 'name':switchcasedefault, - 'scope':blkscope}) + # Check if it's worth adding a break. If there's no executable + # code, there's no point. However, this check is insufficient. + # It misses SEF expressions. For that reason, this is best left + # up to a later optimizer that knows about SEF. But we do a + # preliminary elimination here. + if self.does_something(blk): + switchcasedefault = brk + self.breakstack[-1][2] = True + else: + # TODO: Keep checking until there's output-generating code. + # For example, this isn't optimized due to the semicolon: + # switch(1) { ; default: 1; } + if blk and blk[0]['nt'] == '@' and blk[0]['name'] == switchcasedefault: + switchcasedefault = None + del blk[0] + + if switchcasedefault is not None: + prelude.append({'nt':'JUMP', 't':None, 'name':switchcasedefault, + 'scope':blkscope}) last = self.breakstack.pop() if last[2]: blk.append({'nt':'@', 'name':brk, 'scope':blkscope})