refactoring assignments codegen

This commit is contained in:
Irmen de Jong 2020-08-20 15:18:24 +02:00
parent c144d4e501
commit 6f3b2749b0
6 changed files with 127 additions and 190 deletions

View File

@ -328,7 +328,7 @@ open class Assignment(var target: AssignTarget, var value: Expression, override
return("Assignment(target: $target, value: $value, pos=$position)")
}
val isInplace: Boolean
val isAugmentable: Boolean
get() {
val binExpr = value as? BinaryExpression
if(binExpr!=null) {

View File

@ -1025,36 +1025,6 @@ $counterVar .byte 0""")
internal fun translateFunctionCall(functionCall: FunctionCall) =
functioncallAsmGen.translateFunctionCall(functionCall)
internal fun assignFromEvalResult(target: AssignTarget) =
assignmentAsmGen.assignFromEvalResult(target)
fun assignFromByteConstant(target: AssignTarget, value: Short) =
assignmentAsmGen.assignFromByteConstant(target, value)
fun assignFromWordConstant(target: AssignTarget, value: Int) =
assignmentAsmGen.assignFromWordConstant(target, value)
fun assignFromFloatConstant(target: AssignTarget, value: Double) =
assignmentAsmGen.assignFromFloatConstant(target, value)
fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromByteVariable(target, variable)
fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromWordVariable(target, variable)
fun assignFromAddressOf(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromAddressOf(target, variable)
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
assignmentAsmGen.assignFromFloatVariable(target, variable)
fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
assignmentAsmGen.assignFromRegister(target, register)
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
assignmentAsmGen.assignToRegister(reg, value, identifier)
}

View File

@ -3,10 +3,7 @@ package prog8.compiler.target.c64.codegen
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.VarDecl
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
@ -18,17 +15,38 @@ import prog8.compiler.toHex
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translate(assign: Assignment) {
if(assign.isInplace)
translateNormalAssignment(assign) // TODO generate better code here for in-place assignments
else
translateNormalAssignment(assign)
when {
assign.value is NumericLiteralValue -> translateConstantValueAssignment(assign)
assign.value is IdentifierReference -> translateVariableAssignment(assign)
assign.isAugmentable -> {
println("TODO: optimize augmentable assignment ${assign.position}") // TODO
translateOtherAssignment(assign) // TODO generate better code here for augmentable assignments
}
else -> translateOtherAssignment(assign)
}
}
// old code-generation below:
// eventually, all of this should have been replaced by newer more optimized code.
private fun translateNormalAssignment(assign: Assignment) {
when (assign.value) {
is NumericLiteralValue -> {
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
if(value!=null) {
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
} else if(identifier!=null) {
val name = asmgen.asmIdentifierName(identifier)
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
}
}
private fun translateVariableAssignment(assign: Assignment) {
val identifier = assign.value as IdentifierReference
when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, identifier)
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, identifier)
DataType.FLOAT -> assignFromFloatVariable(assign.target, identifier)
in PassByReferenceDatatypes -> assignFromAddressOf(assign.target, identifier)
else -> throw AssemblyError("unsupported assignment target type $type")
}
}
private fun translateConstantValueAssignment(assign: Assignment) {
val numVal = assign.value as NumericLiteralValue
when (numVal.type) {
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
@ -37,14 +55,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("weird numval type")
}
}
is IdentifierReference -> {
when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference)
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference)
DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
else -> throw AssemblyError("unsupported assignment target type $type")
}
}
private fun translateOtherAssignment(assign: Assignment) {
when (assign.value) {
is AddressOf -> {
val identifier = (assign.value as AddressOf).identifier
assignFromAddressOf(assign.target, identifier)
@ -60,7 +73,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
}
else -> {
throw AssemblyError("missing asm gen for memread assignment into ${assign.target}")
asmgen.translateExpression(read.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
assignFromRegister(assign.target, CpuRegister.A)
}
}
}
@ -120,10 +135,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
else -> throw AssemblyError("assignment value type should have been handled elsewhere")
}
}
internal fun assignFromEvalResult(target: AssignTarget) {
private fun assignFromEvalResult(target: AssignTarget) {
val targetIdent = target.identifier
when {
targetIdent != null -> {
@ -152,8 +168,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
target.memoryAddress != null -> {
asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
asmgen.out(" inx")
storeByteViaRegisterAInMemoryAddress("$ESTACK_LO_HEX,x", target.memoryAddress)
}
target.arrayindexed != null -> {
val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT)
@ -166,7 +182,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
private fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
val struct = name.memberOfStruct(program.namespace)
@ -204,7 +220,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
private fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmIdentifierName(variable)
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
@ -234,7 +250,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
private fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmIdentifierName(variable)
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
@ -265,7 +281,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
private fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
val sourceName = asmgen.asmIdentifierName(variable)
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
@ -296,16 +312,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" lda $sourceName | sta $targetName")
}
else -> {
asmgen.translateExpression(addressExpr)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
ldy $ESTACK_HI_HEX,x
sta (+) +1
sty (+) +2
lda $sourceName
+ sta ${'$'}ffff ; modified
""")
storeByteViaRegisterAInMemoryAddress(sourceName, target.memoryAddress)
}
}
}
@ -313,7 +320,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
private fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
when {
@ -372,7 +379,39 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
when {
addressLv != null -> asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
addressExpr is IdentifierReference -> {
val targetName = asmgen.asmIdentifierName(addressExpr)
asmgen.out("""
lda $targetName
sta ${C64Zeropage.SCRATCH_W1}
lda $targetName+1
sta ${C64Zeropage.SCRATCH_W1+1}
lda $ldaInstructionArg
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y""")
}
else -> {
asmgen.translateExpression(addressExpr)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta ${C64Zeropage.SCRATCH_W1}
lda $ESTACK_HI_HEX,x
sta ${C64Zeropage.SCRATCH_W1+1}
lda $ldaInstructionArg
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y""")
}
}
}
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
// this is optimized for register A.
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
val registerName = register.name.toLowerCase()
@ -381,48 +420,35 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
addressExpr is IdentifierReference -> {
val targetName = asmgen.asmIdentifierName(addressExpr)
when (register) {
CpuRegister.A -> asmgen.out("""
ldy $targetName
sty (+) +1
ldy $targetName+1
sty (+) +2
+ sta ${'$'}ffff ; modified""")
CpuRegister.X -> asmgen.out("""
ldy $targetName
sty (+) +1
ldy $targetName+1
sty (+) +2
+ stx ${'$'}ffff ; modified""")
CpuRegister.Y -> asmgen.out("""
lda $targetName
sta (+) +1
lda $targetName+1
sta (+) +2
+ sty ${'$'}ffff ; modified""")
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out("""
ldy $targetName
sty ${C64Zeropage.SCRATCH_W1}
ldy $targetName+1
sty ${C64Zeropage.SCRATCH_W1+1}
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y""")
}
else -> {
asmgen.saveRegister(register)
asmgen.translateExpression(addressExpr)
asmgen.restoreRegister(register)
when (register) {
CpuRegister.A -> asmgen.out(" tay")
CpuRegister.X -> throw AssemblyError("can't use X register here")
CpuRegister.Y -> {}
}
asmgen.restoreRegister(CpuRegister.A)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta (+) +1
lda $ESTACK_HI_HEX,x
sta (+) +2
+ sty ${'$'}ffff ; modified
""")
ldy $ESTACK_LO_HEX,x
sty ${C64Zeropage.SCRATCH_W1}
ldy $ESTACK_HI_HEX,x
sty ${C64Zeropage.SCRATCH_W1+1}
ldy #0
sta (${C64Zeropage.SCRATCH_W1}),y""")
}
}
}
internal fun assignFromWordConstant(target: AssignTarget, word: Int) {
private fun assignFromWordConstant(target: AssignTarget, word: Int) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
when {
@ -467,17 +493,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromByteConstant(target: AssignTarget, byte: Short) {
private fun assignFromByteConstant(target: AssignTarget, byte: Short) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
val targetMemory = target.memoryAddress
when {
targetIdent != null -> {
val targetName = asmgen.asmIdentifierName(targetIdent)
asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
}
target.memoryAddress != null -> {
asmgen.out(" ldy #${byte.toHex()}")
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
targetMemory != null -> {
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", targetMemory)
}
targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index
@ -495,7 +521,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromFloatConstant(target: AssignTarget, float: Double) {
private fun assignFromFloatConstant(target: AssignTarget, float: Double) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
if (float == 0.0) {
@ -594,7 +620,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
internal fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
private fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
val targetIdent = target.identifier
val targetArrayIdx = target.arrayindexed
if (address != null) {
@ -607,8 +633,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
target.memoryAddress != null -> {
asmgen.out(" ldy ${address.toHex()}")
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
storeByteViaRegisterAInMemoryAddress(address.toHex(), target.memoryAddress)
}
targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index
@ -631,8 +656,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sta $targetName""")
}
target.memoryAddress != null -> {
asmgen.out(" ldy $sourceName")
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
storeByteViaRegisterAInMemoryAddress(sourceName, target.memoryAddress)
}
targetArrayIdx != null -> {
val index = targetArrayIdx.arrayspec.index
@ -663,13 +687,4 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
throw AssemblyError("weird array type")
}
}
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
if(value!=null) {
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
} else if(identifier!=null) {
val name = asmgen.asmIdentifierName(identifier)
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
}
}
}

View File

@ -140,10 +140,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
asmgen.out("""
lda $sourceName
sta (+) +1
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1}
lda $sourceName+1
sta (+) +2
+ lda ${'$'}ffff ; modified
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1+1}
ldy #0
lda (${C64MachineDefinition.C64Zeropage.SCRATCH_W1}),y
sta $ESTACK_LO_HEX,x
dex""")
}

View File

@ -5,6 +5,7 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.compiler.AssemblyError
@ -107,48 +108,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val paramVar = parameter.value
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
target.linkParents(value.parent)
when (value) {
is NumericLiteralValue -> {
// optimize when the argument is a constant literal
when(parameter.value.type) {
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
else -> throw AssemblyError("weird parameter datatype")
}
}
is IdentifierReference -> {
// optimize when the argument is a variable
when (parameter.value.type) {
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
in PassByReferenceDatatypes -> asmgen.assignFromAddressOf(target, value)
else -> throw AssemblyError("weird parameter datatype")
}
}
is DirectMemoryRead -> {
when(value.addressExpression) {
is NumericLiteralValue -> {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
asmgen.assignFromMemoryByte(target, address, null)
}
is IdentifierReference -> {
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
}
else -> {
asmgen.translateExpression(value.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
asmgen.assignFromRegister(target, CpuRegister.A)
}
}
}
else -> {
asmgen.translateExpression(value)
asmgen.assignFromEvalResult(target)
}
}
val assign = Assignment(target, value, value.position)
assign.linkParents(value.parent)
asmgen.translate(assign)
}
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {

View File

@ -7,21 +7,10 @@ main {
sub start() {
c64scr.print_ub(5)
c64.CHROUT('\n')
return
ubyte A=5
uword clr = $d020
@(clr+1) = A
c64scr.print_ub(5)
c64.CHROUT('\n')
goto start
c64scr.print_ub(5)
c64.CHROUT('\n')
exit(11)
c64scr.print_ub(5)
c64.CHROUT('\n')
; uword xx = @(clr+1)
}
}