mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
optimizer
This commit is contained in:
parent
eb58119b97
commit
ab71a15007
@ -78,16 +78,16 @@ class PlyParser:
|
||||
previous_stmt = None
|
||||
for node in module.all_nodes():
|
||||
if isinstance(node, Scope):
|
||||
previous_stmt = None
|
||||
if node.nodes and isinstance(node.parent, (Block, Subroutine)):
|
||||
self._check_last_statement_is_return(node.nodes[-1])
|
||||
elif isinstance(node, SubCall):
|
||||
if isinstance(node.target, SymbolName):
|
||||
subdef = node.my_scope().lookup(node.target.name)
|
||||
self.check_subroutine_arguments(node, subdef) # type: ignore
|
||||
if isinstance(subdef, Subroutine):
|
||||
self.check_subroutine_arguments(node, subdef)
|
||||
elif isinstance(node, Subroutine):
|
||||
# the previous statement (if any) must be a Goto or Return
|
||||
if previous_stmt and not isinstance(previous_stmt, (Goto, Return, VarDef, Subroutine)):
|
||||
if not isinstance(previous_stmt, (Scope, Goto, Return, VarDef, Subroutine)):
|
||||
raise ParseError("statement preceding subroutine must be a goto or return or another subroutine", node.sourceref)
|
||||
elif isinstance(node, IncrDecr):
|
||||
if isinstance(node.target, SymbolName):
|
||||
@ -172,7 +172,7 @@ class PlyParser:
|
||||
lvalue_types = set(datatype_of(lv, node.my_scope()) for lv in node.left.nodes)
|
||||
if len(lvalue_types) == 1:
|
||||
_, newright = coerce_constant_value(lvalue_types.pop(), node.right, node.sourceref)
|
||||
if isinstance(newright, (LiteralValue, Expression)):
|
||||
if isinstance(newright, (Register, LiteralValue, Expression)):
|
||||
node.right = newright
|
||||
else:
|
||||
raise TypeError("invalid coerced constant type", newright)
|
||||
@ -187,14 +187,14 @@ class PlyParser:
|
||||
def reduce_right(assign: Assignment) -> Assignment:
|
||||
if isinstance(assign.right, Assignment):
|
||||
right = reduce_right(assign.right)
|
||||
assign.left.extend(right.left)
|
||||
assign.left.nodes.extend(right.left.nodes)
|
||||
assign.right = right.right
|
||||
return assign
|
||||
|
||||
for node in module.all_nodes(Assignment):
|
||||
if isinstance(node.right, Assignment):
|
||||
multi = reduce_right(node)
|
||||
assert multi is node and len(multi.left) > 1 and not isinstance(multi.right, Assignment)
|
||||
assert multi is node and len(multi.left.nodes) > 1 and not isinstance(multi.right, Assignment)
|
||||
|
||||
@no_type_check
|
||||
def apply_directive_options(self, module: Module) -> None:
|
||||
@ -324,9 +324,8 @@ class PlyParser:
|
||||
@no_type_check
|
||||
def _get_subroutine_usages_from_goto(self, usages: Dict[Tuple[str, str], Set[str]],
|
||||
goto: Goto, parent_scope: Scope) -> None:
|
||||
target = goto.target.target
|
||||
if isinstance(target, SymbolName):
|
||||
usages[(parent_scope.name, target.name)].add(str(goto.sourceref))
|
||||
if isinstance(goto.target, SymbolName):
|
||||
usages[(parent_scope.name, goto.target.name)].add(str(goto.sourceref))
|
||||
self._get_subroutine_usages_from_expression(usages, goto.condition, parent_scope)
|
||||
|
||||
def _get_subroutine_usages_from_return(self, usages: Dict[Tuple[str, str], Set[str]],
|
||||
|
113
il65/optimize.py
113
il65/optimize.py
@ -5,8 +5,9 @@ This is the optimizer that applies various optimizations to the parse tree.
|
||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
"""
|
||||
|
||||
from typing import List, no_type_check
|
||||
from .plyparse import Module, Subroutine, Block, Directive, Assignment, AugAssignment, Goto, Expression, IncrDecr,\
|
||||
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue
|
||||
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue, Scope
|
||||
from .plylex import print_warning, print_bold
|
||||
|
||||
|
||||
@ -18,14 +19,13 @@ class Optimizer:
|
||||
def optimize(self) -> None:
|
||||
self.num_warnings = 0
|
||||
self.optimize_assignments()
|
||||
return # XXX fix all methods below
|
||||
#self.combine_assignments_into_multi()
|
||||
#self.optimize_multiassigns()
|
||||
#self.remove_unused_subroutines()
|
||||
#self.optimize_compare_with_zero()
|
||||
self.combine_assignments_into_multi()
|
||||
self.optimize_multiassigns()
|
||||
self.remove_unused_subroutines()
|
||||
self.optimize_goto_compare_with_zero()
|
||||
# @todo join multiple incr/decr of same var into one (if value stays < 256)
|
||||
# @todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
|
||||
#self.remove_empty_blocks()
|
||||
self.remove_empty_blocks()
|
||||
|
||||
def optimize_assignments(self) -> None:
|
||||
# remove assignment statements that do nothing (A=A)
|
||||
@ -64,10 +64,10 @@ class Optimizer:
|
||||
|
||||
def combine_assignments_into_multi(self) -> None:
|
||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||
for block, parent in self.module.all_scopes():
|
||||
for scope in self.module.all_nodes(Scope):
|
||||
rvalue = None
|
||||
assignments = []
|
||||
for stmt in list(block.nodes):
|
||||
assignments = [] # type: List[Assignment]
|
||||
for stmt in list(scope.nodes):
|
||||
if isinstance(stmt, Assignment):
|
||||
if assignments:
|
||||
if stmt.right == rvalue:
|
||||
@ -77,8 +77,8 @@ class Optimizer:
|
||||
# replace the first assignment by a multi-assign with all the others
|
||||
for assignment in assignments[1:]:
|
||||
print("{}: joined with previous assignment".format(assignment.sourceref))
|
||||
assignments[0].left.extend(assignment.left)
|
||||
block.scope.remove_node(assignment)
|
||||
assignments[0].left.nodes.extend(assignment.left.nodes)
|
||||
scope.remove_node(assignment)
|
||||
rvalue = None
|
||||
assignments.clear()
|
||||
else:
|
||||
@ -88,18 +88,19 @@ class Optimizer:
|
||||
rvalue = None
|
||||
assignments.clear()
|
||||
|
||||
@no_type_check
|
||||
def optimize_multiassigns(self) -> None:
|
||||
# optimize multi-assign statements (remove duplicate targets, optimize order)
|
||||
for block, parent in self.module.all_scopes():
|
||||
for assignment in block.nodes:
|
||||
if isinstance(assignment, Assignment) and len(assignment.left) > 1:
|
||||
# remove duplicates
|
||||
lvalues = set(assignment.left)
|
||||
if len(lvalues) != len(assignment.left):
|
||||
print("{}: removed duplicate assignment targets".format(assignment.sourceref))
|
||||
# @todo change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any)
|
||||
assignment.left = list(lvalues)
|
||||
for assignment in self.module.all_nodes(Assignment):
|
||||
if len(assignment.left.nodes) > 1:
|
||||
# remove duplicates
|
||||
lvalues = set(assignment.left.nodes)
|
||||
if len(lvalues) != len(assignment.left.nodes):
|
||||
print("{}: removed duplicate assignment targets".format(assignment.sourceref))
|
||||
# @todo change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any)
|
||||
assignment.left.nodes = list(lvalues)
|
||||
|
||||
@no_type_check
|
||||
def remove_unused_subroutines(self) -> None:
|
||||
# some symbols are used by the emitted assembly code from the code generator,
|
||||
# and should never be removed or the assembler will fail
|
||||
@ -107,47 +108,45 @@ class Optimizer:
|
||||
"c64flt.GIVUAYF", "c64flt.copy_mflt", "c64flt.float_add_one", "c64flt.float_sub_one",
|
||||
"c64flt.float_add_SW1_to_XY", "c64flt.float_sub_SW1_from_XY"}
|
||||
num_discarded = 0
|
||||
for sub, parent in self.module.all_scopes():
|
||||
if isinstance(sub, Subroutine):
|
||||
usages = self.module.subroutine_usage[(parent.name, sub.name)]
|
||||
if not usages and parent.name + '.' + sub.name not in never_remove:
|
||||
parent.scope.remove_node(sub)
|
||||
num_discarded += 1
|
||||
for sub in self.module.all_nodes(Subroutine):
|
||||
usages = self.module.subroutine_usage[(sub.parent.name, sub.name)]
|
||||
if not usages and sub.parent.name + '.' + sub.name not in never_remove:
|
||||
sub.parent.remove_node(sub)
|
||||
num_discarded += 1
|
||||
if num_discarded:
|
||||
print("discarded {:d} unused subroutines".format(num_discarded))
|
||||
|
||||
def optimize_compare_with_zero(self) -> None:
|
||||
@no_type_check
|
||||
def optimize_goto_compare_with_zero(self) -> None:
|
||||
# a conditional goto that compares a value with zero will be simplified
|
||||
# the comparison operator and rvalue (0) will be removed and the if-status changed accordingly
|
||||
for block, parent in self.module.all_scopes():
|
||||
if block.scope:
|
||||
for goto in block.all_nodes(Goto):
|
||||
if isinstance(goto.condition, Expression):
|
||||
print("NOT IMPLEMENTED YET: optimize goto conditionals", goto.condition) # @todo
|
||||
# if cond and isinstance(cond.rvalue, (int, float)) and cond.rvalue.value == 0:
|
||||
# simplified = False
|
||||
# if cond.ifstatus in ("true", "ne"):
|
||||
# if cond.comparison_op == "==":
|
||||
# # if_true something == 0 -> if_not something
|
||||
# cond.ifstatus = "not"
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.comparison_op == "!=":
|
||||
# # if_true something != 0 -> if_true something
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.ifstatus in ("not", "eq"):
|
||||
# if cond.comparison_op == "==":
|
||||
# # if_not something == 0 -> if_true something
|
||||
# cond.ifstatus = "true"
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.comparison_op == "!=":
|
||||
# # if_not something != 0 -> if_not something
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# if simplified:
|
||||
# print("{}: simplified comparison with zero".format(stmt.sourceref))
|
||||
for goto in self.module.all_nodes(Goto):
|
||||
if isinstance(goto.condition, Expression):
|
||||
print("NOT IMPLEMENTED YET: optimize goto conditionals", goto.condition) # @todo
|
||||
# if cond and isinstance(cond.rvalue, (int, float)) and cond.rvalue.value == 0:
|
||||
# simplified = False
|
||||
# if cond.ifstatus in ("true", "ne"):
|
||||
# if cond.comparison_op == "==":
|
||||
# # if_true something == 0 -> if_not something
|
||||
# cond.ifstatus = "not"
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.comparison_op == "!=":
|
||||
# # if_true something != 0 -> if_true something
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.ifstatus in ("not", "eq"):
|
||||
# if cond.comparison_op == "==":
|
||||
# # if_not something == 0 -> if_true something
|
||||
# cond.ifstatus = "true"
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# elif cond.comparison_op == "!=":
|
||||
# # if_not something != 0 -> if_not something
|
||||
# cond.comparison_op, cond.rvalue = "", None
|
||||
# simplified = True
|
||||
# if simplified:
|
||||
# print("{}: simplified comparison with zero".format(stmt.sourceref))
|
||||
|
||||
def remove_empty_blocks(self) -> None:
|
||||
# remove blocks without name and without address, or that are empty
|
||||
|
@ -382,12 +382,6 @@ class Subroutine(AstNode):
|
||||
raise ValueError("subroutine must have either a scope or an address, not both")
|
||||
|
||||
|
||||
@attr.s(cmp=False, repr=False)
|
||||
class Goto(AstNode):
|
||||
# one or two subnodes: target (SymbolName, int or Dereference) and optionally: condition (Expression)
|
||||
if_stmt = attr.ib(default=None)
|
||||
|
||||
|
||||
@attr.s(cmp=True, slots=True)
|
||||
class LiteralValue(AstNode):
|
||||
# no subnodes.
|
||||
@ -499,6 +493,20 @@ class Expression(AstNode):
|
||||
print(tree(self, 0))
|
||||
|
||||
|
||||
@attr.s(cmp=False, repr=False)
|
||||
class Goto(AstNode):
|
||||
# one or two subnodes: target (SymbolName, int or Dereference) and optionally: condition (Expression)
|
||||
if_stmt = attr.ib(default=None)
|
||||
|
||||
@property
|
||||
def target(self) -> Union[SymbolName, int, Dereference]:
|
||||
return self.nodes[0] # type: ignore
|
||||
|
||||
@property
|
||||
def condition(self) -> Expression:
|
||||
return self.nodes[1] if len(self.nodes) == 2 else None # type: ignore
|
||||
|
||||
|
||||
@attr.s(cmp=False, slots=True)
|
||||
class CallArgument(AstNode):
|
||||
# one subnode: the value (Expression)
|
||||
@ -614,19 +622,20 @@ class AssignmentTargets(AstNode):
|
||||
@attr.s(cmp=False, slots=True, repr=False)
|
||||
class Assignment(AstNode):
|
||||
# can be single- or multi-assignment
|
||||
# has two subnodes: left (=AssignmentTargets) and right (=Expression or another Assignment but those will be converted to multi assign)
|
||||
# has two subnodes: left (=AssignmentTargets) and right (=reg/literal/expr
|
||||
# or another Assignment but those will be converted to multi assign)
|
||||
|
||||
@property
|
||||
def left(self) -> AssignmentTargets:
|
||||
return self.nodes[0] # type: ignore
|
||||
|
||||
@property
|
||||
def right(self) -> Union[LiteralValue, Expression]:
|
||||
def right(self) -> Union[Register, LiteralValue, Expression]:
|
||||
return self.nodes[1] # type: ignore
|
||||
|
||||
@right.setter
|
||||
def right(self, rvalue: Union[LiteralValue, Expression]) -> None:
|
||||
assert isinstance(rvalue, (LiteralValue, Expression))
|
||||
def right(self, rvalue: Union[Register, LiteralValue, Expression]) -> None:
|
||||
assert isinstance(rvalue, (Register, LiteralValue, Expression))
|
||||
self.nodes[1] = rvalue
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user