mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
more optimizations
This commit is contained in:
parent
197a4e503e
commit
2a662ba256
@ -192,10 +192,12 @@ class PlyParser:
|
|||||||
self.handle_internal_error(x, "process_expressions of node {}".format(node))
|
self.handle_internal_error(x, "process_expressions of node {}".format(node))
|
||||||
elif isinstance(node, IncrDecr) and node.howmuch not in (0, 1):
|
elif isinstance(node, IncrDecr) and node.howmuch not in (0, 1):
|
||||||
_, node.howmuch = coerce_constant_value(datatype_of(node.target, node.my_scope()), node.howmuch, node.sourceref)
|
_, node.howmuch = coerce_constant_value(datatype_of(node.target, node.my_scope()), node.howmuch, node.sourceref)
|
||||||
|
attr.validate(node)
|
||||||
elif isinstance(node, VarDef):
|
elif isinstance(node, VarDef):
|
||||||
dtype = DataType.WORD if node.vartype == VarType.MEMORY else node.datatype
|
dtype = DataType.WORD if node.vartype == VarType.MEMORY else node.datatype
|
||||||
try:
|
try:
|
||||||
_, node.value = coerce_constant_value(dtype, node.value, node.sourceref) # type: ignore
|
_, node.value = coerce_constant_value(dtype, node.value, node.sourceref) # type: ignore
|
||||||
|
attr.validate(node)
|
||||||
except OverflowError as x:
|
except OverflowError as x:
|
||||||
raise ParseError(str(x), node.sourceref) from None
|
raise ParseError(str(x), node.sourceref) from None
|
||||||
elif isinstance(node, Assignment):
|
elif isinstance(node, Assignment):
|
||||||
|
@ -13,33 +13,33 @@ from ..compile import Zeropage
|
|||||||
|
|
||||||
|
|
||||||
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
|
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
|
||||||
pass
|
|
||||||
out("\v\t\t\t; " + stmt.lineref)
|
out("\v\t\t\t; " + stmt.lineref)
|
||||||
out("\v; @todo assignment")
|
out("\v; @todo assignment")
|
||||||
# @todo assignment
|
# @todo assignment
|
||||||
|
|
||||||
|
|
||||||
def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> None:
|
def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> None:
|
||||||
# for instance: value += 3 (value = 1-255)
|
# for instance: value += 3 (value = 0-255 for now)
|
||||||
# left: Register, SymbolName, or Dereference. right: Expression/LiteralValue
|
# left: Register, SymbolName, or Dereference. right: Expression/LiteralValue
|
||||||
out("\v\t\t\t; " + stmt.lineref)
|
out("\v\t\t\t; " + stmt.lineref)
|
||||||
out("\v; @todo aug-assignment")
|
|
||||||
lvalue = stmt.left
|
lvalue = stmt.left
|
||||||
rvalue = stmt.right
|
rvalue = stmt.right
|
||||||
if isinstance(lvalue, Register):
|
if isinstance(lvalue, Register):
|
||||||
if isinstance(rvalue, LiteralValue):
|
if isinstance(rvalue, LiteralValue):
|
||||||
if type(rvalue.value) is int:
|
if type(rvalue.value) is int:
|
||||||
if 0 < rvalue.value < 256:
|
if 0 <= rvalue.value <= 255:
|
||||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope)
|
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope)
|
||||||
else:
|
else:
|
||||||
raise CodeError("incr/decr value should be 1..255", rvalue)
|
raise CodeError("assignment value must be 0..255", rvalue)
|
||||||
else:
|
else:
|
||||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||||
elif isinstance(rvalue, SymbolName):
|
elif isinstance(rvalue, SymbolName):
|
||||||
symdef = scope.lookup(rvalue.name)
|
symdef = scope.lookup(rvalue.name)
|
||||||
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST and symdef.datatype.isinteger():
|
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST and symdef.datatype.isinteger():
|
||||||
# @todo check value range
|
if 0 <= symdef.value.const_num_val() <= 255:
|
||||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
||||||
|
else:
|
||||||
|
raise CodeError("assignment value must be 0..255", rvalue)
|
||||||
else:
|
else:
|
||||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||||
elif isinstance(rvalue, Register):
|
elif isinstance(rvalue, Register):
|
||||||
@ -55,16 +55,16 @@ def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) ->
|
|||||||
def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
|
def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
|
||||||
r_str = rname or to_hex(rvalue)
|
r_str = rname or to_hex(rvalue)
|
||||||
if operator == "+=":
|
if operator == "+=":
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
out("\vadc #" + r_str)
|
out("\vadc #" + r_str)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
out("\vadc #" + r_str)
|
out("\vadc #" + r_str)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
@ -73,16 +73,16 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
||||||
elif operator == "-=":
|
elif operator == "-=":
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
out("\vsbc #" + r_str)
|
out("\vsbc #" + r_str)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
out("\vsbc #" + r_str)
|
out("\vsbc #" + r_str)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
@ -91,14 +91,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
||||||
elif operator == "&=":
|
elif operator == "&=":
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vand #" + r_str)
|
out("\vand #" + r_str)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vand #" + r_str)
|
out("\vand #" + r_str)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vand #" + r_str)
|
out("\vand #" + r_str)
|
||||||
@ -106,14 +106,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
||||||
elif operator == "|=":
|
elif operator == "|=":
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vora #" + r_str)
|
out("\vora #" + r_str)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vora #" + r_str)
|
out("\vora #" + r_str)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vora #" + r_str)
|
out("\vora #" + r_str)
|
||||||
@ -121,14 +121,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
||||||
elif operator == "^=":
|
elif operator == "^=":
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\veor #" + r_str)
|
out("\veor #" + r_str)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\veor #" + r_str)
|
out("\veor #" + r_str)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\veor #" + r_str)
|
out("\veor #" + r_str)
|
||||||
@ -143,14 +143,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
for _ in range(min(8, times)):
|
for _ in range(min(8, times)):
|
||||||
out("\vlsr a")
|
out("\vlsr a")
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
@ -165,14 +165,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
else:
|
else:
|
||||||
for _ in range(min(8, times)):
|
for _ in range(min(8, times)):
|
||||||
out("\vasl a")
|
out("\vasl a")
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
shifts_A(rvalue.value)
|
shifts_A(rvalue.value)
|
||||||
@ -183,21 +183,21 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
|||||||
|
|
||||||
def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue: Register, scope: Scope) -> None:
|
def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue: Register, scope: Scope) -> None:
|
||||||
if operator == "+=":
|
if operator == "+=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vclc")
|
out("\vclc")
|
||||||
@ -206,21 +206,21 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
||||||
elif operator == "-=":
|
elif operator == "-=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vsec")
|
out("\vsec")
|
||||||
@ -229,19 +229,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
||||||
elif operator == "&=":
|
elif operator == "&=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
@ -249,19 +249,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
||||||
elif operator == "|=":
|
elif operator == "|=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
@ -269,19 +269,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
||||||
elif operator == "^=":
|
elif operator == "^=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
@ -289,19 +289,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
||||||
elif operator == ">>=":
|
elif operator == ">>=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
@ -309,19 +309,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
||||||
elif operator == "<<=":
|
elif operator == "<<=":
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
if rvalue.name not in REGISTER_BYTES:
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
|
||||||
if lvalue.register == "A":
|
if lvalue.name == "A":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
elif lvalue.register == "X":
|
elif lvalue.name == "X":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtxa")
|
out("\vtxa")
|
||||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
out("\vtax")
|
out("\vtax")
|
||||||
elif lvalue.register == "Y":
|
elif lvalue.name == "Y":
|
||||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
with preserving_registers({'A'}, scope, out):
|
with preserving_registers({'A'}, scope, out):
|
||||||
out("\vtya")
|
out("\vtya")
|
||||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
@ -186,19 +186,19 @@ class AssemblyGenerator:
|
|||||||
assignment = Assignment(sourceref=stmt.sourceref)
|
assignment = Assignment(sourceref=stmt.sourceref)
|
||||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||||
assignment.nodes.append(stmt.value_A)
|
assignment.nodes.append(stmt.value_A)
|
||||||
generate_assignment(out, assignment)
|
generate_assignment(out, assignment, scope)
|
||||||
if stmt.value_X:
|
if stmt.value_X:
|
||||||
reg = Register(name="X", sourceref=stmt.sourceref)
|
reg = Register(name="X", sourceref=stmt.sourceref)
|
||||||
assignment = Assignment(sourceref=stmt.sourceref)
|
assignment = Assignment(sourceref=stmt.sourceref)
|
||||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||||
assignment.nodes.append(stmt.value_X)
|
assignment.nodes.append(stmt.value_X)
|
||||||
generate_assignment(out, assignment)
|
generate_assignment(out, assignment, scope)
|
||||||
if stmt.value_Y:
|
if stmt.value_Y:
|
||||||
reg = Register(name="Y", sourceref=stmt.sourceref)
|
reg = Register(name="Y", sourceref=stmt.sourceref)
|
||||||
assignment = Assignment(sourceref=stmt.sourceref)
|
assignment = Assignment(sourceref=stmt.sourceref)
|
||||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||||
assignment.nodes.append(stmt.value_Y)
|
assignment.nodes.append(stmt.value_Y)
|
||||||
generate_assignment(out, assignment)
|
generate_assignment(out, assignment, scope)
|
||||||
out("\vrts")
|
out("\vrts")
|
||||||
elif isinstance(stmt, InlineAssembly):
|
elif isinstance(stmt, InlineAssembly):
|
||||||
out("\n\v; inline asm, " + stmt.lineref)
|
out("\n\v; inline asm, " + stmt.lineref)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
||||||
This is the code generator for the in-place incr and decr instructions.
|
This is the code generator for the in-place incr and decr instructions.
|
||||||
Incrementing or decrementing variables by 1 (or another constant amount)
|
Incrementing or decrementing variables by a small value 0..255 (for integers)
|
||||||
is quite frequent and this generates assembly code tweaked for this case.
|
is quite frequent and this generates assembly code tweaked for this case.
|
||||||
|
|
||||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
@ -16,6 +16,8 @@ from . import CodeError, preserving_registers
|
|||||||
def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None:
|
def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None:
|
||||||
assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0
|
assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0
|
||||||
assert stmt.operator in ("++", "--")
|
assert stmt.operator in ("++", "--")
|
||||||
|
if stmt.howmuch == 0:
|
||||||
|
return
|
||||||
target = stmt.target # one of Register/SymbolName/Dereference, or a VarDef
|
target = stmt.target # one of Register/SymbolName/Dereference, or a VarDef
|
||||||
if isinstance(target, SymbolName):
|
if isinstance(target, SymbolName):
|
||||||
symdef = scope.lookup(target.name)
|
symdef = scope.lookup(target.name)
|
||||||
|
@ -6,7 +6,7 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, List, Callable, Any
|
from typing import Dict, List, Callable, Any, no_type_check
|
||||||
from ..plyparse import Block, VarType, VarDef, LiteralValue
|
from ..plyparse import Block, VarType, VarDef, LiteralValue
|
||||||
from ..datatypes import DataType, STRING_DATATYPES
|
from ..datatypes import DataType, STRING_DATATYPES
|
||||||
from . import to_hex, to_mflpt5, CodeError
|
from . import to_hex, to_mflpt5, CodeError
|
||||||
@ -122,7 +122,7 @@ def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> No
|
|||||||
if vardef.datatype == DataType.FLOAT:
|
if vardef.datatype == DataType.FLOAT:
|
||||||
out("\v{:s} = {}".format(vardef.name, _numeric_value_str(vardef.value)))
|
out("\v{:s} = {}".format(vardef.name, _numeric_value_str(vardef.value)))
|
||||||
elif vardef.datatype in (DataType.BYTE, DataType.WORD):
|
elif vardef.datatype in (DataType.BYTE, DataType.WORD):
|
||||||
assert isinstance(vardef.value.value, int)
|
assert isinstance(vardef.value.value, int) # type: ignore
|
||||||
out("\v{:s} = {:s}".format(vardef.name, _numeric_value_str(vardef.value, True)))
|
out("\v{:s} = {:s}".format(vardef.name, _numeric_value_str(vardef.value, True)))
|
||||||
elif vardef.datatype.isstring():
|
elif vardef.datatype.isstring():
|
||||||
# a const string is just a string variable in the generated assembly
|
# a const string is just a string variable in the generated assembly
|
||||||
@ -132,16 +132,16 @@ def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> No
|
|||||||
out("; memory mapped variables")
|
out("; memory mapped variables")
|
||||||
for vardef in vars_by_vartype.get(VarType.MEMORY, []):
|
for vardef in vars_by_vartype.get(VarType.MEMORY, []):
|
||||||
# create a definition for variables at a specific place in memory (memory-mapped)
|
# create a definition for variables at a specific place in memory (memory-mapped)
|
||||||
assert isinstance(vardef.value.value, int)
|
assert isinstance(vardef.value.value, int) # type: ignore
|
||||||
if vardef.datatype.isnumeric():
|
if vardef.datatype.isnumeric():
|
||||||
assert vardef.size == [1]
|
assert vardef.size == [1]
|
||||||
out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), vardef.datatype.name.lower()))
|
out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), vardef.datatype.name.lower())) # type: ignore
|
||||||
elif vardef.datatype == DataType.BYTEARRAY:
|
elif vardef.datatype == DataType.BYTEARRAY:
|
||||||
assert len(vardef.size) == 1
|
assert len(vardef.size) == 1
|
||||||
out("\v{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value.value), vardef.size[0]))
|
out("\v{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value.value), vardef.size[0])) # type: ignore
|
||||||
elif vardef.datatype == DataType.WORDARRAY:
|
elif vardef.datatype == DataType.WORDARRAY:
|
||||||
assert len(vardef.size) == 1
|
assert len(vardef.size) == 1
|
||||||
out("\v{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value.value), vardef.size[0]))
|
out("\v{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value.value), vardef.size[0])) # type: ignore
|
||||||
elif vardef.datatype == DataType.MATRIX:
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
assert len(vardef.size) in (2, 3)
|
assert len(vardef.size) in (2, 3)
|
||||||
if len(vardef.size) == 2:
|
if len(vardef.size) == 2:
|
||||||
@ -149,8 +149,8 @@ def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> No
|
|||||||
elif len(vardef.size) == 3:
|
elif len(vardef.size) == 3:
|
||||||
comment = "matrix of {:d} by {:d}, interleave {:d}".format(vardef.size[0], vardef.size[1], vardef.size[2])
|
comment = "matrix of {:d} by {:d}, interleave {:d}".format(vardef.size[0], vardef.size[1], vardef.size[2])
|
||||||
else:
|
else:
|
||||||
raise CodeError("matrix size should be 2 or 3 numbers")
|
raise CodeError("matrix size must be 2 or 3 numbers")
|
||||||
out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), comment))
|
out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), comment)) # type: ignore
|
||||||
else:
|
else:
|
||||||
raise CodeError("invalid var type")
|
raise CodeError("invalid var type")
|
||||||
out("; normal variables - initial values will be set by init code")
|
out("; normal variables - initial values will be set by init code")
|
||||||
@ -199,6 +199,7 @@ def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> No
|
|||||||
out("")
|
out("")
|
||||||
|
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def _generate_string_var(out: Callable, vardef: VarDef) -> None:
|
def _generate_string_var(out: Callable, vardef: VarDef) -> None:
|
||||||
if vardef.datatype == DataType.STRING:
|
if vardef.datatype == DataType.STRING:
|
||||||
# 0-terminated string
|
# 0-terminated string
|
||||||
|
136
il65/optimize.py
136
il65/optimize.py
@ -6,10 +6,12 @@ eliminates statements that have no effect, optimizes calculations etc.
|
|||||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List, no_type_check
|
from typing import List, no_type_check, Union
|
||||||
from .plyparse import AstNode, Module, Subroutine, Block, Directive, Assignment, AugAssignment, Goto, Expression, IncrDecr,\
|
from .plyparse import AstNode, Module, Subroutine, Block, Directive, Assignment, AugAssignment, Goto, Expression, IncrDecr,\
|
||||||
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue, Scope, Register
|
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue, Scope, Register, SymbolName, \
|
||||||
from .plylex import print_warning, print_bold
|
Dereference, TargetRegisters, VarDef
|
||||||
|
from .plylex import print_warning, print_bold, SourceRef
|
||||||
|
from .datatypes import DataType
|
||||||
|
|
||||||
|
|
||||||
class Optimizer:
|
class Optimizer:
|
||||||
@ -19,10 +21,13 @@ class Optimizer:
|
|||||||
|
|
||||||
def optimize(self) -> None:
|
def optimize(self) -> None:
|
||||||
self.num_warnings = 0
|
self.num_warnings = 0
|
||||||
|
self.create_aug_assignments()
|
||||||
self.optimize_assignments()
|
self.optimize_assignments()
|
||||||
self.remove_superfluous_assignments()
|
self.remove_superfluous_assignments()
|
||||||
self.combine_assignments_into_multi()
|
self.combine_assignments_into_multi()
|
||||||
self.optimize_multiassigns()
|
self.optimize_multiassigns()
|
||||||
|
# @todo optimize some simple multiplications into shifts (A*=8 -> A<<3)
|
||||||
|
# @todo optimize addition with self into shift 1 (A+=A -> A<<=1)
|
||||||
self.remove_unused_subroutines()
|
self.remove_unused_subroutines()
|
||||||
self.optimize_goto_compare_with_zero()
|
self.optimize_goto_compare_with_zero()
|
||||||
self.join_incrdecrs()
|
self.join_incrdecrs()
|
||||||
@ -30,8 +35,106 @@ class Optimizer:
|
|||||||
self.remove_empty_blocks()
|
self.remove_empty_blocks()
|
||||||
|
|
||||||
def join_incrdecrs(self) -> None:
|
def join_incrdecrs(self) -> None:
|
||||||
# @todo joins multiple incr/decr of same var into one (if value stays < 256 which ...)
|
for scope in self.module.all_nodes(Scope):
|
||||||
pass
|
incrdecrs = [] # type: List[IncrDecr]
|
||||||
|
target = None
|
||||||
|
for node in list(scope.nodes):
|
||||||
|
if isinstance(node, IncrDecr):
|
||||||
|
if target is None:
|
||||||
|
target = node.target
|
||||||
|
incrdecrs.append(node)
|
||||||
|
continue
|
||||||
|
if self._same_target(target, node.target):
|
||||||
|
incrdecrs.append(node)
|
||||||
|
continue
|
||||||
|
if len(incrdecrs) > 1:
|
||||||
|
# optimize...
|
||||||
|
replaced = False
|
||||||
|
total = 0
|
||||||
|
for i in incrdecrs:
|
||||||
|
if i.operator == "++":
|
||||||
|
total += i.howmuch
|
||||||
|
else:
|
||||||
|
total -= i.howmuch
|
||||||
|
if total == 0:
|
||||||
|
replaced = True
|
||||||
|
for x in incrdecrs:
|
||||||
|
scope.remove_node(x)
|
||||||
|
else:
|
||||||
|
is_float = False
|
||||||
|
if isinstance(target, SymbolName):
|
||||||
|
symdef = target.my_scope().lookup(target.name)
|
||||||
|
if isinstance(symdef, VarDef) and symdef.datatype == DataType.FLOAT:
|
||||||
|
is_float = True
|
||||||
|
elif isinstance(target, Dereference):
|
||||||
|
is_float = target.datatype == DataType.FLOAT
|
||||||
|
if is_float:
|
||||||
|
replaced = True
|
||||||
|
for x in incrdecrs[1:]:
|
||||||
|
scope.remove_node(x)
|
||||||
|
incrdecr = self._make_incrdecr(incrdecrs[0], target, abs(total), "++" if total >= 0 else "--")
|
||||||
|
scope.replace_node(incrdecrs[0], incrdecr)
|
||||||
|
elif 0 < total <= 255:
|
||||||
|
replaced = True
|
||||||
|
for x in incrdecrs[1:]:
|
||||||
|
scope.remove_node(x)
|
||||||
|
incrdecr = self._make_incrdecr(incrdecrs[0], target, total, "++")
|
||||||
|
scope.replace_node(incrdecrs[0], incrdecr)
|
||||||
|
elif -255 <= total < 0:
|
||||||
|
replaced = True
|
||||||
|
total = -total
|
||||||
|
for x in incrdecrs[1:]:
|
||||||
|
scope.remove_node(x)
|
||||||
|
incrdecr = self._make_incrdecr(incrdecrs[0], target, total, "--")
|
||||||
|
scope.replace_node(incrdecrs[0], incrdecr)
|
||||||
|
if replaced:
|
||||||
|
self.num_warnings += 1
|
||||||
|
print_warning("{}: merged a sequence of incr/decrs or augmented assignments".format(incrdecrs[0].sourceref))
|
||||||
|
incrdecrs.clear()
|
||||||
|
target = None
|
||||||
|
if isinstance(node, IncrDecr):
|
||||||
|
incrdecrs.append(node)
|
||||||
|
target = node.target
|
||||||
|
|
||||||
|
def _same_target(self, node1: Union[TargetRegisters, Register, SymbolName, Dereference],
|
||||||
|
node2: Union[TargetRegisters, Register, SymbolName, Dereference]) -> bool:
|
||||||
|
if isinstance(node1, Register) and isinstance(node2, Register) and node1.name == node2.name:
|
||||||
|
return True
|
||||||
|
if isinstance(node1, SymbolName) and isinstance(node2, SymbolName) and node1.name == node2.name:
|
||||||
|
return True
|
||||||
|
if isinstance(node1, Dereference) and isinstance(node2, Dereference) and node1.operand == node2.operand:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
|
def create_aug_assignments(self) -> None:
|
||||||
|
# create augmented assignments from regular assignment that only refers to the lvalue
|
||||||
|
# A=A+10, A=10+A -> A+=10, A=A*4, A=4*A -> A*=4, etc
|
||||||
|
for assignment in self.module.all_nodes(Assignment):
|
||||||
|
if len(assignment.left.nodes) > 1:
|
||||||
|
continue
|
||||||
|
if not isinstance(assignment.right, Expression) or assignment.right.unary:
|
||||||
|
continue
|
||||||
|
expr = assignment.right
|
||||||
|
if expr.operator in ('-', '/', '//', '**', '<<', '>>', '&'): # non-associative operators
|
||||||
|
if isinstance(expr.right, (LiteralValue, SymbolName)) and self._same_target(assignment.left.nodes[0], expr.left):
|
||||||
|
num_val = expr.right.const_num_val()
|
||||||
|
operator = expr.operator + '='
|
||||||
|
aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator)
|
||||||
|
assignment.my_scope().replace_node(assignment, aug_assign)
|
||||||
|
continue
|
||||||
|
if expr.operator not in ('+', '*', '|', '^'): # associative operators
|
||||||
|
continue
|
||||||
|
if isinstance(expr.right, (LiteralValue, SymbolName)) and self._same_target(assignment.left.nodes[0], expr.left):
|
||||||
|
num_val = expr.right.const_num_val()
|
||||||
|
operator = expr.operator + '='
|
||||||
|
aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator)
|
||||||
|
assignment.my_scope().replace_node(assignment, aug_assign)
|
||||||
|
elif isinstance(expr.left, (LiteralValue, SymbolName)) and self._same_target(assignment.left.nodes[0], expr.right):
|
||||||
|
num_val = expr.left.const_num_val()
|
||||||
|
operator = expr.operator + '='
|
||||||
|
aug_assign = self._make_aug_assign(assignment, assignment.left.nodes[0], num_val, operator)
|
||||||
|
assignment.my_scope().replace_node(assignment, aug_assign)
|
||||||
|
|
||||||
def remove_superfluous_assignments(self) -> None:
|
def remove_superfluous_assignments(self) -> None:
|
||||||
# remove consecutive assignment statements to the same target, only keep the last value (only if its a constant!)
|
# remove consecutive assignment statements to the same target, only keep the last value (only if its a constant!)
|
||||||
@ -49,15 +152,13 @@ class Optimizer:
|
|||||||
|
|
||||||
def optimize_assignments(self) -> None:
|
def optimize_assignments(self) -> None:
|
||||||
# remove assignment statements that do nothing (A=A)
|
# remove assignment statements that do nothing (A=A)
|
||||||
# and augmented assignments that have no effect (x+=0, x-=0, x/=1, x//=1, x*=1)
|
# remove augmented assignments that have no effect (x+=0, x-=0, x/=1, x//=1, x*=1)
|
||||||
# convert augmented assignments to simple incr/decr if possible (A+=10 => A++ by 10)
|
# convert augmented assignments to simple incr/decr if possible (A+=10 => A++ by 10)
|
||||||
# simplify some calculations (x*=0, x**=0) to simple constant value assignment
|
# simplify some calculations (x*=0, x**=0) to simple constant value assignment
|
||||||
# @todo remove or simplify logical aug assigns like A |= 0, A |= true, A |= false (or perhaps turn them into byte values first?)
|
# @todo remove or simplify logical aug assigns like A |= 0, A |= true, A |= false (or perhaps turn them into byte values first?)
|
||||||
for assignment in self.module.all_nodes():
|
for assignment in self.module.all_nodes():
|
||||||
if isinstance(assignment, Assignment):
|
if isinstance(assignment, Assignment):
|
||||||
if any(lv != assignment.right for lv in assignment.left.nodes):
|
if all(lv == assignment.right for lv in assignment.left.nodes):
|
||||||
assignment.left.nodes = [lv for lv in assignment.left.nodes if lv != assignment.right]
|
|
||||||
if not assignment.left:
|
|
||||||
assignment.my_scope().remove_node(assignment)
|
assignment.my_scope().remove_node(assignment)
|
||||||
self.num_warnings += 1
|
self.num_warnings += 1
|
||||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||||
@ -110,6 +211,23 @@ class Optimizer:
|
|||||||
new_assignment.nodes.append(value)
|
new_assignment.nodes.append(value)
|
||||||
return new_assignment
|
return new_assignment
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
|
def _make_aug_assign(self, old_assign: Assignment, target: Union[TargetRegisters, Register, SymbolName, Dereference],
|
||||||
|
value: Union[int, float], operator: str) -> AugAssignment:
|
||||||
|
a = AugAssignment(operator=operator, sourceref=old_assign.sourceref)
|
||||||
|
a.nodes.append(target)
|
||||||
|
a.nodes.append(LiteralValue(value=value, sourceref=old_assign.sourceref))
|
||||||
|
a.parent = old_assign.parent
|
||||||
|
return a
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
|
def _make_incrdecr(self, old_stmt: AstNode, target: Union[TargetRegisters, Register, SymbolName, Dereference],
|
||||||
|
howmuch: Union[int, float], operator: str) -> AugAssignment:
|
||||||
|
a = IncrDecr(operator=operator, howmuch=howmuch, sourceref=old_stmt.sourceref)
|
||||||
|
a.nodes.append(target)
|
||||||
|
a.parent = old_stmt.parent
|
||||||
|
return a
|
||||||
|
|
||||||
def combine_assignments_into_multi(self) -> None:
|
def combine_assignments_into_multi(self) -> None:
|
||||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||||
for scope in self.module.all_nodes(Scope):
|
for scope in self.module.all_nodes(Scope):
|
||||||
|
59
todo.ill
59
todo.ill
@ -1,24 +1,57 @@
|
|||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
|
var .float flt
|
||||||
|
|
||||||
start:
|
start:
|
||||||
XY=0
|
|
||||||
XY+=255
|
|
||||||
XY+=256
|
|
||||||
XY+=257
|
|
||||||
XY-=255
|
|
||||||
XY-=256
|
|
||||||
XY-=257
|
|
||||||
|
|
||||||
|
Y+=10
|
||||||
|
Y-=5
|
||||||
|
Y-=8
|
||||||
|
Y--
|
||||||
|
|
||||||
XY++
|
flt+=2
|
||||||
XY+=1
|
flt+=2
|
||||||
XY+=1 ; @todo? (optimize) join with previous
|
flt+=2
|
||||||
XY+=0 ; is removed.
|
flt+=2
|
||||||
|
flt+=2
|
||||||
|
|
||||||
|
X=0
|
||||||
|
X+=5
|
||||||
|
X=X+22
|
||||||
|
X=33+X
|
||||||
|
X-=3
|
||||||
|
X=X-4
|
||||||
|
X=X-5
|
||||||
|
X=8*X
|
||||||
|
X=24|X
|
||||||
|
X=X^66
|
||||||
|
X+=250
|
||||||
|
X+=5
|
||||||
|
X-=100
|
||||||
|
X-=50
|
||||||
|
X-=5
|
||||||
|
|
||||||
|
Y=Y
|
||||||
|
X=X
|
||||||
|
A=A
|
||||||
|
|
||||||
|
X++
|
||||||
|
X--
|
||||||
|
X+=1
|
||||||
|
X+=1 ; @todo? (optimize) join with previous
|
||||||
|
X+=0 ; is removed.
|
||||||
A+=0 ; is removed.
|
A+=0 ; is removed.
|
||||||
Y+=0 ; is removed.
|
Y+=0 ; is removed.
|
||||||
XY+=1 ; @todo? (optimize) join with previous
|
X+=1 ; @todo? (optimize) join with previous
|
||||||
XY+=1 ; @todo? (optimize) join with previous
|
X+=1 ; @todo? (optimize) join with previous
|
||||||
|
|
||||||
|
;@todo float incrdecr/augassign
|
||||||
|
;flt += 0.1
|
||||||
|
;flt += 1.1
|
||||||
|
;flt += 10.1
|
||||||
|
;flt += 100.1
|
||||||
|
;flt += 1000.1
|
||||||
|
|
||||||
return 44
|
return 44
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user