diff --git a/il65/codegen.py b/il65/codegen.py index 1b9f696ca..e2ad59660 100644 --- a/il65/codegen.py +++ b/il65/codegen.py @@ -1127,9 +1127,45 @@ class CodeGenerator: else: raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word elif operator == ">>=": - raise CodeError("can not yet shift a variable amount") # XXX + if rvalue.datatype != DataType.BYTE: + raise CodeError("can only shift by a byte value", str(rvalue)) + r_str = rvalue.name or Parser.to_hex(rvalue.address) + if lvalue.register == "A": + self.p("\t\tlsr " + r_str) + elif lvalue.register == "X": + self.p("\t\tpha") + self.p("\t\ttxa") + self.p("\t\tlsr " + r_str) + self.p("\t\ttax") + self.p("\t\tpla") + elif lvalue.register == "Y": + self.p("\t\tpha") + self.p("\t\ttya") + self.p("\t\tlsr " + r_str) + self.p("\t\ttay") + self.p("\t\tpla") + else: + raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word elif operator == "<<=": - raise CodeError("can not yet shift a variable amount") # XXX + if rvalue.datatype != DataType.BYTE: + raise CodeError("can only shift by a byte value", str(rvalue)) + r_str = rvalue.name or Parser.to_hex(rvalue.address) + if lvalue.register == "A": + self.p("\t\tasl " + r_str) + elif lvalue.register == "X": + self.p("\t\tpha") + self.p("\t\ttxa") + self.p("\t\tasl " + r_str) + self.p("\t\ttax") + self.p("\t\tpla") + elif lvalue.register == "Y": + self.p("\t\tpha") + self.p("\t\ttya") + self.p("\t\tasl " + r_str) + self.p("\t\ttay") + self.p("\t\tpla") + else: + 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) @@ -1225,43 +1261,53 @@ class CodeGenerator: else: raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word elif operator == ">>=": - if rvalue.value != 1: - raise CodeError("can only shift 1 bit for now") # XXX - if lvalue.register == "A": - self.p("\t\tlsr a") - elif lvalue.register == "X": - self.p("\t\tpha") - self.p("\t\ttxa") - self.p("\t\tlsr a") - self.p("\t\ttax") - self.p("\t\tpla") - elif lvalue.register == "Y": - self.p("\t\tpha") - self.p("\t\ttya") - self.p("\t\tlsr a") - self.p("\t\ttay") - self.p("\t\tpla") - else: - raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word + 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": + self.p("\t\tpha") + self.p("\t\ttxa") + shifts_A(rvalue.value) + self.p("\t\ttax") + self.p("\t\tpla") + elif lvalue.register == "Y": + self.p("\t\tpha") + self.p("\t\ttya") + shifts_A(rvalue.value) + self.p("\t\ttay") + self.p("\t\tpla") + else: + raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word elif operator == "<<=": - if rvalue.value != 1: - raise CodeError("can only shift 1 bit for now") # XXX - if lvalue.register == "A": - self.p("\t\tasl a") - elif lvalue.register == "X": - self.p("\t\tpha") - self.p("\t\ttxa") - self.p("\t\tasl a") - self.p("\t\ttax") - self.p("\t\tpla") - elif lvalue.register == "Y": - self.p("\t\tpha") - self.p("\t\ttya") - self.p("\t\tasl a") - self.p("\t\ttay") - self.p("\t\tpla") - else: - raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word + 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": + self.p("\t\tpha") + self.p("\t\ttxa") + shifts_A(rvalue.value) + self.p("\t\ttax") + self.p("\t\tpla") + elif lvalue.register == "Y": + self.p("\t\tpha") + self.p("\t\ttya") + shifts_A(rvalue.value) + self.p("\t\ttay") + self.p("\t\tpla") + 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 == "+=": diff --git a/il65/parse.py b/il65/parse.py index 2b7843995..01b1b6dac 100644 --- a/il65/parse.py +++ b/il65/parse.py @@ -313,13 +313,13 @@ class Parser: for name, value in stmt.arguments or []: assert name is not None, "all call arguments should have a name or be matched on a named parameter" assignment = self.parse_assignment(name, value) + assignment.sourceref = stmt.sourceref.copy() if assignment.leftvalues[0].datatype != DataType.BYTE: if isinstance(assignment.right, IntegerValue) and assignment.right.constant: # a call that doesn't expect a BYTE argument but gets one, converted from a 1-byte string most likely if value.startswith("'") and value.endswith("'"): self.print_warning("possible problematic string to byte conversion (use a .text var instead?)") if not assignment.is_identity(): - assignment.sourceref = stmt.sourceref.copy() # @todo why set this? stmt.desugared_call_arguments.append(assignment) if all(not isinstance(v, RegisterValue) for r, v in stmt.outputvars or []): # if none of the output variables are registers, we can simply generate the assignments without issues @@ -978,15 +978,11 @@ class Parser: return InplaceIncrStmt(l_value, r_value, self.sourceref) elif r_value.value < 0: return InplaceDecrStmt(l_value, r_value.negative(), self.sourceref) - else: - self.print_warning("incr with zero, ignored") else: if r_value.value > 0: return InplaceDecrStmt(l_value, r_value, self.sourceref) elif r_value.value < 0: return InplaceIncrStmt(l_value, r_value.negative(), self.sourceref) - else: - self.print_warning("decr with zero, ignored") return AugmentedAssignmentStmt(l_value, operator, r_value, self.sourceref) def parse_return(self, line: str) -> ReturnStmt: @@ -1315,6 +1311,7 @@ class Optimizer: def optimize(self) -> ParseResult: print("\noptimizing parse tree") for block in self.parsed.all_blocks(): + self.remove_augmentedassign_incrdecr_nops(block) self.remove_identity_assigns(block) self.combine_assignments_into_multi(block) self.optimize_multiassigns(block) @@ -1322,6 +1319,23 @@ class Optimizer: self.optimize_compare_with_zero(block) return self.parsed + def remove_augmentedassign_incrdecr_nops(self, block: Block) -> None: + have_removed_stmts = False + for index, stmt in enumerate(list(block.statements)): + if isinstance(stmt, AugmentedAssignmentStmt): + if isinstance(stmt.right, (IntegerValue, FloatValue)): + if stmt.right.value == 0 and stmt.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="): + print("{}: removed statement that has no effect".format(stmt.sourceref)) + have_removed_stmts = True + block.statements[index] = None + if stmt.right.value >= 8 and stmt.operator in ("<<=", ">>="): + print("{}: shifting that many times always results in zero".format(stmt.sourceref)) + new_stmt = AssignmentStmt(stmt.leftvalues, IntegerValue(0, stmt.sourceref), stmt.sourceref) + block.statements[index] = new_stmt + if have_removed_stmts: + # remove the Nones + block.statements = [s for s in block.statements if s is not None] + def optimize_compare_with_zero(self, block: Block) -> None: # a conditional goto that compares a value to zero will be simplified # the comparison operator and rvalue (0) will be removed and the if-status changed accordingly @@ -1402,7 +1416,6 @@ class Optimizer: def remove_unused_subroutines(self, block: Block) -> None: # some symbols are used by the emitted assembly code from the code generator, # and should never be removed or the assembler will fail - # @todo make this dynamic never_remove = {"c64.FREADUY", "c64.FTOMEMXY", "c64.FADD", "c64.FSUB", "c64flt.GIVUAYF", "c64flt.copy_mflt", "c64flt.float_add_one", "c64flt.float_sub_one", "c64flt.float_add_SW1_to_XY", "c64flt.float_sub_SW1_from_XY"} diff --git a/todo.ill b/todo.ill index 73990beda..06d8a5dcc 100644 --- a/todo.ill +++ b/todo.ill @@ -5,14 +5,40 @@ output prg,basic import "c64lib" ~ main { - const num = 44 - var var1 =22 - var .word wvar1 = 22 + const num = 2 + var var1 =2 + var .word wvar1 = 2 + start + + X <<= var1 + X >>= var1 + var1 ++ var1 += num X++ X+=num + X+=0 + X-=0 + X <<= Y + A <<= X + Y <<= A + X <<= 0 + X <<= 33333 + X >>= 33333 + X <<= 2 + X <<= 7 + X <<= 8 + X <<= 22 + X >>= 0 + X >>= 1 + X >>= 2 + X >>= 7 + X >>= 8 + X >>= 22 + ;XY <<= 0 + ;XY <<= 1 + ;XY <<= 2 asm {