zero division checks and more optimizations

This commit is contained in:
Irmen de Jong 2018-01-24 00:41:50 +01:00
parent 6573368a69
commit 31c6186245
9 changed files with 368 additions and 559 deletions

View File

@ -102,7 +102,7 @@ class PlyParser:
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST: if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST:
raise ParseError("cannot modify a constant", target.sourceref) raise ParseError("cannot modify a constant", target.sourceref)
elif isinstance(node, AugAssignment): elif isinstance(node, AugAssignment):
# the assignment target must be a variable # the assignment target must not be a constant
if isinstance(node.left, SymbolName): if isinstance(node.left, SymbolName):
symdef = node.my_scope().lookup(node.left.name) symdef = node.my_scope().lookup(node.left.name)
if isinstance(symdef, VarDef): if isinstance(symdef, VarDef):
@ -111,6 +111,10 @@ class PlyParser:
elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}: elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
raise ParseError("cannot modify that datatype ({:s}) in this way" raise ParseError("cannot modify that datatype ({:s}) in this way"
.format(symdef.datatype.name.lower()), node.sourceref) .format(symdef.datatype.name.lower()), node.sourceref)
# check for divide by (constant) zero
if node.operator in ("/=", "//="):
if isinstance(node.right, LiteralValue) and node.right.value == 0:
raise ParseError("division by zero", node.right.sourceref)
previous_stmt = node previous_stmt = node
def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None: def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None:

View File

@ -46,6 +46,9 @@ class DataType(enum.Enum):
def isnumeric(self) -> bool: def isnumeric(self) -> bool:
return self.value in (1, 2, 3) return self.value in (1, 2, 3)
def isinteger(self) -> bool:
return self.value in (1, 2)
def isarray(self) -> bool: def isarray(self) -> bool:
return self.value in (4, 5, 6) return self.value in (4, 5, 6)

View File

@ -6,18 +6,320 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
""" """
from typing import Callable from typing import Callable
from ..plyparse import AstNode, Scope, VarDef, Dereference, Register, TargetRegisters,\ from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef
LiteralValue, Assignment, AugAssignment from . import CodeError, preserving_registers, to_hex
from ..datatypes import REGISTER_BYTES, REGISTER_WORDS, VarType, DataType
from ..compile import Zeropage
def generate_assignment(out: Callable, stmt: Assignment) -> None: def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
assert stmt.right is not 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
# 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 rvalue = stmt.right
if isinstance(stmt.right, LiteralValue): if isinstance(lvalue, Register):
rvalue = stmt.right.value if isinstance(rvalue, LiteralValue):
# @todo if type(rvalue.value) is int:
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope)
else:
raise CodeError("integer literal or variable required for now", rvalue) # XXX
elif isinstance(rvalue, SymbolName):
symdef = scope.lookup(rvalue.name)
if isinstance(symdef, VarDef) and symdef.datatype.isinteger():
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
else:
raise CodeError("integer literal or variable required for now", rvalue) # XXX
elif isinstance(rvalue, Register):
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, scope)
else:
# @todo Register += symbolname / dereference , _generate_aug_reg_mem?
raise CodeError("invalid rvalue for augmented assignment on register", rvalue)
else:
raise CodeError("augmented assignment only implemented for registers for now", stmt.sourceref) # XXX
def generate_aug_assignment(out: Callable, stmt: AugAssignment) -> None: def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
assert stmt.right is not None r_str = rname or to_hex(rvalue)
pass # @todo if operator == "+=":
if lvalue.register == "A":
out("\vclc")
out("\vadc #" + r_str)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vclc")
out("\vadc #" + r_str)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vclc")
out("\vadc #" + r_str)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
elif operator == "-=":
if lvalue.register == "A":
out("\vsec")
out("\vsbc #" + r_str)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vsec")
out("\vsbc #" + r_str)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vsec")
out("\vsbc #" + r_str)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
elif operator == "&=":
if lvalue.register == "A":
out("\vand #" + r_str)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vand #" + r_str)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vand #" + r_str)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
elif operator == "|=":
if lvalue.register == "A":
out("\vora #" + r_str)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vora #" + r_str)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vora #" + r_str)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
elif operator == "^=":
if lvalue.register == "A":
out("\veor #" + r_str)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\veor #" + r_str)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\veor #" + r_str)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
elif operator == ">>=":
if rvalue.value > 0:
def shifts_A(times: int) -> None:
if times >= 8:
out("\vlda #0")
else:
for _ in range(min(8, times)):
out("\vlsr a")
if lvalue.register == "A":
shifts_A(rvalue.value)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
shifts_A(rvalue.value)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
shifts_A(rvalue.value)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
elif operator == "<<=":
if rvalue.value > 0:
def shifts_A(times: int) -> None:
if times >= 8:
out("\vlda #0")
else:
for _ in range(min(8, times)):
out("\vasl a")
if lvalue.register == "A":
shifts_A(rvalue.value)
elif lvalue.register == "X":
with preserving_registers({'A'}, scope, out):
out("\vtxa")
shifts_A(rvalue.value)
out("\vtax")
elif lvalue.register == "Y":
with preserving_registers({'A'}, scope, out):
out("\vtya")
shifts_A(rvalue.value)
out("\vtay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
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:
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)))
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)))
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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vclc")
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
elif operator == "-=":
if rvalue.register 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)))
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)))
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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vsec")
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
elif operator == "&=":
if rvalue.register 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)))
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
out("\vst{:s} {:s}".format(rvalue.register.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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
elif operator == "|=":
if rvalue.register 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)))
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
out("\vst{:s} {:s}".format(rvalue.register.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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
elif operator == "^=":
if rvalue.register 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)))
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
out("\vst{:s} {:s}".format(rvalue.register.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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
elif operator == ">>=":
if rvalue.register 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)))
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
out("\vst{:s} {:s}".format(rvalue.register.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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
elif operator == "<<=":
if rvalue.register 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)))
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
out("\vst{:s} {:s}".format(rvalue.register.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)))
with preserving_registers({'A'}, scope, out):
out("\vtya")
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
out("\vtay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word

View File

@ -211,9 +211,9 @@ class AssemblyGenerator:
elif isinstance(stmt, SubCall): elif isinstance(stmt, SubCall):
generate_subcall(out, stmt) generate_subcall(out, stmt)
elif isinstance(stmt, Assignment): elif isinstance(stmt, Assignment):
generate_assignment(out, stmt) generate_assignment(out, stmt, scope)
elif isinstance(stmt, AugAssignment): elif isinstance(stmt, AugAssignment):
generate_aug_assignment(out, stmt) generate_aug_assignment(out, stmt, scope)
elif isinstance(stmt, Directive): elif isinstance(stmt, Directive):
if stmt.name == "breakpoint": if stmt.name == "breakpoint":
# put a marker in the source so that we can generate a list of breakpoints later # put a marker in the source so that we can generate a list of breakpoints later

View File

@ -1,203 +1,9 @@
# old deprecated code # old deprecated code, in the process of moving this to the new emit/... modules
class CodeGenerator: class CodeGenerator:
BREAKPOINT_COMMENT_SIGNATURE = "~~~BREAKPOINT~~~" BREAKPOINT_COMMENT_SIGNATURE = "~~~BREAKPOINT~~~"
BREAKPOINT_COMMENT_DETECTOR = r".(?P<address>\w+)\s+ea\s+nop\s+;\s+{:s}.*".format(BREAKPOINT_COMMENT_SIGNATURE) BREAKPOINT_COMMENT_DETECTOR = r".(?P<address>\w+)\s+ea\s+nop\s+;\s+{:s}.*".format(BREAKPOINT_COMMENT_SIGNATURE)
def generate_incr_or_decr(self, stmt: Union[InplaceIncrStmt, InplaceDecrStmt]) -> None:
assert stmt.value.constant
assert (stmt.value.value is None and stmt.value.name) or stmt.value.value > 0
if stmt.what.datatype != DataType.FLOAT and not stmt.value.name and stmt.value.value > 0xff:
raise CodeError("only supports integer incr/decr by up to 255 for now") # XXX
is_incr = isinstance(stmt, InplaceIncrStmt)
howmuch = stmt.value.value
value_str = stmt.value.name or str(howmuch)
if isinstance(stmt.what, RegisterValue):
reg = stmt.what.register
# note: these operations below are all checked to be ok
if is_incr:
if reg == 'A':
# a += 1..255
self.p("\t\tclc")
self.p("\t\tadc #" + value_str)
elif reg in REGISTER_BYTES:
if howmuch == 1:
# x/y += 1
self.p("\t\tin{:s}".format(reg.lower()))
else:
# x/y += 2..255
with self.preserving_registers({'A'}):
self.p("\t\tt{:s}a".format(reg.lower()))
self.p("\t\tclc")
self.p("\t\tadc #" + value_str)
self.p("\t\tta{:s}".format(reg.lower()))
elif reg == "AX":
# AX += 1..255
self.p("\t\tclc")
self.p("\t\tadc #" + value_str)
self.p("\t\tbcc +")
self.p("\t\tinx")
self.p("+")
elif reg == "AY":
# AY += 1..255
self.p("\t\tclc")
self.p("\t\tadc # " + value_str)
self.p("\t\tbcc +")
self.p("\t\tiny")
self.p("+")
elif reg == "XY":
if howmuch == 1:
# XY += 1
self.p("\t\tinx")
self.p("\t\tbne +")
self.p("\t\tiny")
self.p("+")
else:
# XY += 2..255
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tclc")
self.p("\t\tadc #" + value_str)
self.p("\t\ttax")
self.p("\t\tbcc +")
self.p("\t\tiny")
self.p("+")
else:
raise CodeError("invalid incr register: " + reg)
else:
if reg == 'A':
# a -= 1..255
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
elif reg in REGISTER_BYTES:
if howmuch == 1:
# x/y -= 1
self.p("\t\tde{:s}".format(reg.lower()))
else:
# x/y -= 2..255
with self.preserving_registers({'A'}):
self.p("\t\tt{:s}a".format(reg.lower()))
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
self.p("\t\tta{:s}".format(reg.lower()))
elif reg == "AX":
# AX -= 1..255
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
self.p("\t\tbcs +")
self.p("\t\tdex")
self.p("+")
elif reg == "AY":
# AY -= 1..255
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
self.p("\t\tbcs +")
self.p("\t\tdey")
self.p("+")
elif reg == "XY":
if howmuch == 1:
# XY -= 1
self.p("\t\tcpx #0")
self.p("\t\tbne +")
self.p("\t\tdey")
self.p("+\t\tdex")
else:
# XY -= 2..255
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
self.p("\t\ttax")
self.p("\t\tbcs +")
self.p("\t\tdey")
self.p("+")
else:
raise CodeError("invalid decr register: " + reg)
elif isinstance(stmt.what, (MemMappedValue, IndirectValue)):
what = stmt.what
if isinstance(what, IndirectValue):
if isinstance(what.value, IntegerValue):
what_str = what.value.name or Parser.to_hex(what.value.value)
else:
raise CodeError("invalid incr indirect type", what.value)
else:
what_str = what.name or Parser.to_hex(what.address)
if what.datatype == DataType.BYTE:
if howmuch == 1:
self.p("\t\t{:s} {:s}".format("inc" if is_incr else "dec", what_str))
else:
with self.preserving_registers({'A'}):
self.p("\t\tlda " + what_str)
if is_incr:
self.p("\t\tclc")
self.p("\t\tadc #" + value_str)
else:
self.p("\t\tsec")
self.p("\t\tsbc #" + value_str)
self.p("\t\tsta " + what_str)
elif what.datatype == DataType.WORD:
if howmuch == 1:
# mem.word +=/-= 1
if is_incr:
self.p("\t\tinc " + what_str)
self.p("\t\tbne +")
self.p("\t\tinc {:s}+1".format(what_str))
self.p("+")
else:
with self.preserving_registers({'A'}):
self.p("\t\tlda " + what_str)
self.p("\t\tbne +")
self.p("\t\tdec {:s}+1".format(what_str))
self.p("+\t\tdec " + what_str)
else:
# mem.word +=/-= 2..255
if is_incr:
with self.preserving_registers({'A'}):
self.p("\t\tclc")
self.p("\t\tlda " + what_str)
self.p("\t\tadc #" + value_str)
self.p("\t\tsta " + what_str)
self.p("\t\tbcc +")
self.p("\t\tinc {:s}+1".format(what_str))
self.p("+")
else:
with self.preserving_registers({'A'}):
self.p("\t\tsec")
self.p("\t\tlda " + what_str)
self.p("\t\tsbc #" + value_str)
self.p("\t\tsta " + what_str)
self.p("\t\tbcs +")
self.p("\t\tdec {:s}+1".format(what_str))
self.p("+")
elif what.datatype == DataType.FLOAT:
if howmuch == 1.0:
# special case for +/-1
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
self.p("\t\tldx #<" + what_str)
self.p("\t\tldy #>" + what_str)
if is_incr:
self.p("\t\tjsr c64flt.float_add_one")
else:
self.p("\t\tjsr c64flt.float_sub_one")
elif stmt.value.name:
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
self.p("\t\tlda #<" + stmt.value.name)
self.p("\t\tsta c64.SCRATCH_ZPWORD1")
self.p("\t\tlda #>" + stmt.value.name)
self.p("\t\tsta c64.SCRATCH_ZPWORD1+1")
self.p("\t\tldx #<" + what_str)
self.p("\t\tldy #>" + what_str)
if is_incr:
self.p("\t\tjsr c64flt.float_add_SW1_to_XY")
else:
self.p("\t\tjsr c64flt.float_sub_SW1_from_XY")
else:
raise CodeError("incr/decr missing float constant definition")
else:
raise CodeError("cannot in/decrement memory of type " + str(what.datatype), howmuch)
else:
raise CodeError("cannot in/decrement " + str(stmt.what))
def generate_call(self, stmt: CallStmt) -> None: def generate_call(self, stmt: CallStmt) -> None:
self.p("\t\t\t\t\t; " + stmt.lineref) self.p("\t\t\t\t\t; " + stmt.lineref)
if stmt.condition: if stmt.condition:
@ -623,22 +429,7 @@ class CodeGenerator:
branch_emitter(targetstr, False, False) branch_emitter(targetstr, False, False)
generate_result_assignments() generate_result_assignments()
def generate_augmented_assignment(self, stmt: AugmentedAssignmentStmt) -> None:
# for instance: value += 3
lvalue = stmt.leftvalues[0]
rvalue = stmt.right
self.p("\t\t\t\t\t; " + stmt.lineref)
if isinstance(lvalue, RegisterValue):
if isinstance(rvalue, IntegerValue):
self._generate_aug_reg_int(lvalue, stmt.operator, rvalue)
elif isinstance(rvalue, RegisterValue):
self._generate_aug_reg_reg(lvalue, stmt.operator, rvalue)
elif isinstance(rvalue, MemMappedValue):
self._generate_aug_reg_mem(lvalue, stmt.operator, rvalue)
else:
raise CodeError("invalid rvalue for augmented assignment on register", str(rvalue))
else:
raise CodeError("augmented assignment only implemented for registers for now", str(rvalue)) # XXX
def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None: def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None:
r_str = rvalue.name or Parser.to_hex(rvalue.address) r_str = rvalue.name or Parser.to_hex(rvalue.address)
@ -760,281 +551,7 @@ class CodeGenerator:
else: else:
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
def _generate_aug_reg_int(self, lvalue: RegisterValue, operator: str, rvalue: IntegerValue) -> None:
r_str = rvalue.name or Parser.to_hex(rvalue.value)
if operator == "+=":
if lvalue.register == "A":
self.p("\t\tclc")
self.p("\t\tadc #" + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tclc")
self.p("\t\tadc #" + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tclc")
self.p("\t\tadc #" + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
elif operator == "-=":
if lvalue.register == "A":
self.p("\t\tsec")
self.p("\t\tsbc #" + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tsec")
self.p("\t\tsbc #" + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tsec")
self.p("\t\tsbc #" + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
elif operator == "&=":
if lvalue.register == "A":
self.p("\t\tand #" + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tand #" + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tand #" + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
elif operator == "|=":
if lvalue.register == "A":
self.p("\t\tora #" + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tora #" + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tora #" + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
elif operator == "^=":
if lvalue.register == "A":
self.p("\t\teor #" + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\teor #" + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\teor #" + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
elif operator == ">>=":
if rvalue.value > 0:
def shifts_A(times: int) -> None:
if times >= 8:
self.p("\t\tlda #0")
else:
for _ in range(min(8, times)):
self.p("\t\tlsr a")
if lvalue.register == "A":
shifts_A(rvalue.value)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
shifts_A(rvalue.value)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
shifts_A(rvalue.value)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
elif operator == "<<=":
if rvalue.value > 0:
def shifts_A(times: int) -> None:
if times >= 8:
self.p("\t\tlda #0")
else:
for _ in range(min(8, times)):
self.p("\t\tasl a")
if lvalue.register == "A":
shifts_A(rvalue.value)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
shifts_A(rvalue.value)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
shifts_A(rvalue.value)
self.p("\t\ttay")
else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
def _generate_aug_reg_reg(self, lvalue: RegisterValue, operator: str, rvalue: RegisterValue) -> None:
if operator == "+=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tclc")
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tclc")
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tclc")
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
elif operator == "-=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tsec")
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tsec")
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tsec")
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
elif operator == "&=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
elif operator == "|=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
elif operator == "^=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
elif operator == ">>=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
elif operator == "<<=":
if rvalue.register not in REGISTER_BYTES:
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
if lvalue.register == "A":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
elif lvalue.register == "X":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttax")
elif lvalue.register == "Y":
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word
def generate_assignment(self, stmt: AssignmentStmt) -> None: def generate_assignment(self, stmt: AssignmentStmt) -> None:
def unwrap_indirect(iv: IndirectValue) -> MemMappedValue: def unwrap_indirect(iv: IndirectValue) -> MemMappedValue:
@ -1256,58 +773,6 @@ class CodeGenerator:
else: else:
raise CodeError("invalid register " + lv.register) raise CodeError("invalid register " + lv.register)
@contextlib.contextmanager
def preserving_registers(self, registers: Set[str], loads_a_within: bool=False, always_preserve: bool=False):
# this sometimes clobbers a ZP scratch register and is therefore NOT safe to use in interrupts
# see http://6502.org/tutorials/register_preservation.html
if not self.cur_block.preserve_registers and not always_preserve:
yield
return
if registers == {'A'}:
self.p("\t\tpha")
yield
self.p("\t\tpla")
elif registers:
if not loads_a_within:
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
if 'A' in registers:
self.p("\t\tpha")
if 'X' in registers:
self.p("\t\ttxa\n\t\tpha")
if 'Y' in registers:
self.p("\t\ttya\n\t\tpha")
if not loads_a_within:
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
yield
if 'X' in registers and 'Y' in registers:
if 'A' not in registers:
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
self.p("\t\tpla\n\t\ttay")
self.p("\t\tpla\n\t\ttax")
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
else:
self.p("\t\tpla\n\t\ttay")
self.p("\t\tpla\n\t\ttax")
else:
if 'Y' in registers:
if 'A' not in registers:
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
self.p("\t\tpla\n\t\ttay")
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
else:
self.p("\t\tpla\n\t\ttay")
if 'X' in registers:
if 'A' not in registers:
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
self.p("\t\tpla\n\t\ttax")
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
else:
self.p("\t\tpla\n\t\ttax")
if 'A' in registers:
self.p("\t\tpla")
else:
yield
def generate_assign_integer_to_mem(self, lv: MemMappedValue, rvalue: IntegerValue) -> None: def generate_assign_integer_to_mem(self, lv: MemMappedValue, rvalue: IntegerValue) -> None:
if lv.name: if lv.name:
symblock, sym = self.cur_block.lookup(lv.name) symblock, sym = self.cur_block.lookup(lv.name)

View File

@ -43,15 +43,26 @@ class Optimizer:
print_warning("{}: removed statement that has no effect".format(assignment.sourceref)) print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
if isinstance(assignment, AugAssignment): if isinstance(assignment, AugAssignment):
if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)): if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)):
if assignment.right.value == 0 and assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="): if assignment.right.value == 0:
self.num_warnings += 1 if assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="):
print_warning("{}: removed statement that has no effect".format(assignment.sourceref)) self.num_warnings += 1
assignment.my_scope().remove_node(assignment) print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
assignment.my_scope().remove_node(assignment)
elif assignment.operator == "*=":
self.num_warnings += 1
print_warning("{}: statement replaced by = 0".format(assignment.sourceref))
new_assignment = self._make_new_assignment(assignment, 0)
assignment.my_scope().replace_node(assignment, new_assignment)
elif assignment.operator == "**=":
self.num_warnings += 1
print_warning("{}: statement replaced by = 1".format(assignment.sourceref))
new_assignment = self._make_new_assignment(assignment, 1)
assignment.my_scope().replace_node(assignment, new_assignment)
if assignment.right.value >= 8 and assignment.operator in ("<<=", ">>="): if assignment.right.value >= 8 and assignment.operator in ("<<=", ">>="):
print("{}: shifting result is always zero".format(assignment.sourceref)) print("{}: shifting result is always zero".format(assignment.sourceref))
new_stmt = Assignment(sourceref=assignment.sourceref) new_stmt = Assignment(sourceref=assignment.sourceref)
new_stmt.nodes.append(AssignmentTargets(nodes=[assignment.left], sourceref=assignment.sourceref)) new_stmt.nodes.append(AssignmentTargets(nodes=[assignment.left], sourceref=assignment.sourceref))
new_stmt.nodes.append(0) new_stmt.nodes.append(0) # XXX literalvalue?
assignment.my_scope().replace_node(assignment, new_stmt) assignment.my_scope().replace_node(assignment, new_stmt)
if assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256: if assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256:
howmuch = assignment.right howmuch = assignment.right
@ -62,6 +73,22 @@ class Optimizer:
howmuch=howmuch.value, sourceref=assignment.sourceref) howmuch=howmuch.value, sourceref=assignment.sourceref)
new_stmt.target = assignment.left new_stmt.target = assignment.left
assignment.my_scope().replace_node(assignment, new_stmt) assignment.my_scope().replace_node(assignment, new_stmt)
if assignment.operator in ("/=", "//=", "*=") and assignment.right.value == 1:
self.num_warnings += 1
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
assignment.my_scope().remove_node(assignment)
@no_type_check
def _make_new_assignment(self, old_aug_assignment: AugAssignment, constantvalue: int) -> Assignment:
new_assignment = Assignment(sourceref=old_aug_assignment.sourceref)
new_assignment.parent = old_aug_assignment.parent
left = AssignmentTargets(nodes=[old_aug_assignment.left], sourceref=old_aug_assignment.sourceref)
left.parent = new_assignment
new_assignment.nodes.append(left)
value = LiteralValue(value=constantvalue, sourceref=old_aug_assignment.sourceref)
value.parent = new_assignment
new_assignment.nodes.append(value)
return new_assignment
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

View File

@ -86,7 +86,7 @@ t_BITOR = r"\|"
t_BITXOR = r"\^" t_BITXOR = r"\^"
t_BITINVERT = r"~" t_BITINVERT = r"~"
t_IS = r"=" t_IS = r"="
t_AUGASSIGN = r"\+=|-=|/=|\*=|<<=|>>=|&=|\|=|\^=" t_AUGASSIGN = r"\+=|-=|/=|//=|\*=|\*\*=|<<=|>>=|&=|\|=|\^="
t_DECR = r"--" t_DECR = r"--"
t_INCR = r"\+\+" t_INCR = r"\+\+"
t_EQUALS = r"==" t_EQUALS = r"=="

View File

@ -482,6 +482,8 @@ class Expression(AstNode):
estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value)) estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value))
try: try:
return LiteralValue(value=eval(estr, {}, {}), sourceref=sourceref) # type: ignore # safe because of checks above return LiteralValue(value=eval(estr, {}, {}), sourceref=sourceref) # type: ignore # safe because of checks above
except ZeroDivisionError:
raise ParseError("division by zero", sourceref)
except Exception as x: except Exception as x:
raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None

View File

@ -38,8 +38,8 @@ start:
v3t++ v3t++
v3t+=1 v3t+=1
v3t+=1 ; @todo? (optimize) join with previous v3t+=1 ; @todo? (optimize) join with previous
v3t+=0 v3t+=0 ; is removed.
v3t+=1 v3t+=1 ; @todo? (optimize) join with previous
v3t+=1 ; @todo? (optimize) join with previous v3t+=1 ; @todo? (optimize) join with previous
v3t=2.23424 ; @todo store as constant float with generated name, replace value node v3t=2.23424 ; @todo store as constant float with generated name, replace value node
v3t=2.23411 ; @todo store as constant float with generated name, replace value node v3t=2.23411 ; @todo store as constant float with generated name, replace value node
@ -49,7 +49,13 @@ start:
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node ; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node ; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
v3t*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node XY*=2
XY*=3
XY=XY/0 ; @todo zerodiv (during expression to code generation)
XY=XY//0 ; @todo zerodiv (during expression to code generation)
XY*=2.23424 ; @todo store as constant float with generated name, replace value node
XY*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
;v3t*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
; A++ ; A++
; X-- ; X--
; A+=1 ; A+=1