mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +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))
|
||||
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)
|
||||
attr.validate(node)
|
||||
elif isinstance(node, VarDef):
|
||||
dtype = DataType.WORD if node.vartype == VarType.MEMORY else node.datatype
|
||||
try:
|
||||
_, node.value = coerce_constant_value(dtype, node.value, node.sourceref) # type: ignore
|
||||
attr.validate(node)
|
||||
except OverflowError as x:
|
||||
raise ParseError(str(x), node.sourceref) from None
|
||||
elif isinstance(node, Assignment):
|
||||
|
@ -13,33 +13,33 @@ from ..compile import Zeropage
|
||||
|
||||
|
||||
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
|
||||
pass
|
||||
out("\v\t\t\t; " + stmt.lineref)
|
||||
out("\v; @todo assignment")
|
||||
# @todo assignment
|
||||
|
||||
|
||||
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
|
||||
out("\v\t\t\t; " + stmt.lineref)
|
||||
out("\v; @todo aug-assignment")
|
||||
lvalue = stmt.left
|
||||
rvalue = stmt.right
|
||||
if isinstance(lvalue, Register):
|
||||
if isinstance(rvalue, LiteralValue):
|
||||
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)
|
||||
else:
|
||||
raise CodeError("incr/decr value should be 1..255", rvalue)
|
||||
raise CodeError("assignment value must be 0..255", rvalue)
|
||||
else:
|
||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||
elif isinstance(rvalue, SymbolName):
|
||||
symdef = scope.lookup(rvalue.name)
|
||||
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST and symdef.datatype.isinteger():
|
||||
# @todo check value range
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
||||
if 0 <= symdef.value.const_num_val() <= 255:
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
||||
else:
|
||||
raise CodeError("assignment value must be 0..255", rvalue)
|
||||
else:
|
||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||
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:
|
||||
r_str = rname or to_hex(rvalue)
|
||||
if operator == "+=":
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
out("\vclc")
|
||||
out("\vadc #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vclc")
|
||||
out("\vadc #" + r_str)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vclc")
|
||||
@ -73,16 +73,16 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
||||
elif operator == "-=":
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
out("\vsec")
|
||||
out("\vsbc #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vsec")
|
||||
out("\vsbc #" + r_str)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vsec")
|
||||
@ -91,14 +91,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
||||
elif operator == "&=":
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
out("\vand #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vand #" + r_str)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vand #" + r_str)
|
||||
@ -106,14 +106,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
||||
elif operator == "|=":
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
out("\vora #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vora #" + r_str)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vora #" + r_str)
|
||||
@ -121,14 +121,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
||||
elif operator == "^=":
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
out("\veor #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\veor #" + r_str)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\veor #" + r_str)
|
||||
@ -143,14 +143,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
for _ in range(min(8, times)):
|
||||
out("\vlsr a")
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
shifts_A(rvalue.value)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
shifts_A(rvalue.value)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
shifts_A(rvalue.value)
|
||||
@ -165,14 +165,14 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
|
||||
else:
|
||||
for _ in range(min(8, times)):
|
||||
out("\vasl a")
|
||||
if lvalue.register == "A":
|
||||
if lvalue.name == "A":
|
||||
shifts_A(rvalue.value)
|
||||
elif lvalue.register == "X":
|
||||
elif lvalue.name == "X":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
shifts_A(rvalue.value)
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
elif lvalue.name == "Y":
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
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:
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vclc")
|
||||
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vclc")
|
||||
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vclc")
|
||||
@ -206,21 +206,21 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vsec")
|
||||
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vsec")
|
||||
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vsec")
|
||||
@ -229,19 +229,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||
@ -249,19 +249,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||
@ -269,19 +269,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||
@ -289,19 +289,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||
@ -309,19 +309,19 @@ def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
||||
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
|
||||
if lvalue.register == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
if lvalue.name == "A":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "X":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtxa")
|
||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||
out("\vtax")
|
||||
elif lvalue.register == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
elif lvalue.name == "Y":
|
||||
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||
with preserving_registers({'A'}, scope, out):
|
||||
out("\vtya")
|
||||
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||
|
@ -186,19 +186,19 @@ class AssemblyGenerator:
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_A)
|
||||
generate_assignment(out, assignment)
|
||||
generate_assignment(out, assignment, scope)
|
||||
if stmt.value_X:
|
||||
reg = Register(name="X", sourceref=stmt.sourceref)
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_X)
|
||||
generate_assignment(out, assignment)
|
||||
generate_assignment(out, assignment, scope)
|
||||
if stmt.value_Y:
|
||||
reg = Register(name="Y", sourceref=stmt.sourceref)
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_Y)
|
||||
generate_assignment(out, assignment)
|
||||
generate_assignment(out, assignment, scope)
|
||||
out("\vrts")
|
||||
elif isinstance(stmt, InlineAssembly):
|
||||
out("\n\v; inline asm, " + stmt.lineref)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
||||
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.
|
||||
|
||||
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:
|
||||
assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0
|
||||
assert stmt.operator in ("++", "--")
|
||||
if stmt.howmuch == 0:
|
||||
return
|
||||
target = stmt.target # one of Register/SymbolName/Dereference, or a VarDef
|
||||
if isinstance(target, SymbolName):
|
||||
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 typing import Dict, List, Callable, Any
|
||||
from typing import Dict, List, Callable, Any, no_type_check
|
||||
from ..plyparse import Block, VarType, VarDef, LiteralValue
|
||||
from ..datatypes import DataType, STRING_DATATYPES
|
||||
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:
|
||||
out("\v{:s} = {}".format(vardef.name, _numeric_value_str(vardef.value)))
|
||||
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)))
|
||||
elif vardef.datatype.isstring():
|
||||
# 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")
|
||||
for vardef in vars_by_vartype.get(VarType.MEMORY, []):
|
||||
# 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():
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
assert len(vardef.size) in (2, 3)
|
||||
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:
|
||||
comment = "matrix of {:d} by {:d}, interleave {:d}".format(vardef.size[0], vardef.size[1], vardef.size[2])
|
||||
else:
|
||||
raise CodeError("matrix size should be 2 or 3 numbers")
|
||||
out("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value.value), comment))
|
||||
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)) # type: ignore
|
||||
else:
|
||||
raise CodeError("invalid var type")
|
||||
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("")
|
||||
|
||||
|
||||
@no_type_check
|
||||
def _generate_string_var(out: Callable, vardef: VarDef) -> None:
|
||||
if vardef.datatype == DataType.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
|
||||
"""
|
||||
|
||||
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,\
|
||||
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue, Scope, Register
|
||||
from .plylex import print_warning, print_bold
|
||||
datatype_of, coerce_constant_value, AssignmentTargets, LiteralValue, Scope, Register, SymbolName, \
|
||||
Dereference, TargetRegisters, VarDef
|
||||
from .plylex import print_warning, print_bold, SourceRef
|
||||
from .datatypes import DataType
|
||||
|
||||
|
||||
class Optimizer:
|
||||
@ -19,10 +21,13 @@ class Optimizer:
|
||||
|
||||
def optimize(self) -> None:
|
||||
self.num_warnings = 0
|
||||
self.create_aug_assignments()
|
||||
self.optimize_assignments()
|
||||
self.remove_superfluous_assignments()
|
||||
self.combine_assignments_into_multi()
|
||||
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.optimize_goto_compare_with_zero()
|
||||
self.join_incrdecrs()
|
||||
@ -30,8 +35,106 @@ class Optimizer:
|
||||
self.remove_empty_blocks()
|
||||
|
||||
def join_incrdecrs(self) -> None:
|
||||
# @todo joins multiple incr/decr of same var into one (if value stays < 256 which ...)
|
||||
pass
|
||||
for scope in self.module.all_nodes(Scope):
|
||||
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:
|
||||
# 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:
|
||||
# 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)
|
||||
# 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?)
|
||||
for assignment in self.module.all_nodes():
|
||||
if isinstance(assignment, Assignment):
|
||||
if any(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:
|
||||
if all(lv == assignment.right for lv in assignment.left.nodes):
|
||||
assignment.my_scope().remove_node(assignment)
|
||||
self.num_warnings += 1
|
||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||
@ -110,6 +211,23 @@ class Optimizer:
|
||||
new_assignment.nodes.append(value)
|
||||
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:
|
||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||
for scope in self.module.all_nodes(Scope):
|
||||
|
59
todo.ill
59
todo.ill
@ -1,24 +1,57 @@
|
||||
|
||||
~ main {
|
||||
|
||||
var .float flt
|
||||
|
||||
start:
|
||||
XY=0
|
||||
XY+=255
|
||||
XY+=256
|
||||
XY+=257
|
||||
XY-=255
|
||||
XY-=256
|
||||
XY-=257
|
||||
|
||||
Y+=10
|
||||
Y-=5
|
||||
Y-=8
|
||||
Y--
|
||||
|
||||
XY++
|
||||
XY+=1
|
||||
XY+=1 ; @todo? (optimize) join with previous
|
||||
XY+=0 ; is removed.
|
||||
flt+=2
|
||||
flt+=2
|
||||
flt+=2
|
||||
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.
|
||||
Y+=0 ; is removed.
|
||||
XY+=1 ; @todo? (optimize) join with previous
|
||||
XY+=1 ; @todo? (optimize) join with previous
|
||||
X+=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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user