shifting by more than 1 bit possible

This commit is contained in:
Irmen de Jong 2018-01-01 18:57:12 +01:00
parent 57bc7d49bc
commit 5bfca554a4
3 changed files with 132 additions and 47 deletions

View File

@ -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 == "+=":

View File

@ -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"}

View File

@ -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 {