more optimal codegen for some typecasts

This commit is contained in:
Irmen de Jong 2020-09-15 03:26:57 +02:00
parent 32a7cd31da
commit 2a6d9d7e31
4 changed files with 164 additions and 31 deletions

View File

@ -120,17 +120,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
// is TypecastExpression -> {
// if(assign.target.kind == TargetStorageKind.STACK) {
// asmgen.translateExpression(value)
// assignStackValue(assign.target)
// } else {
// println("!!!!TYPECAST to ${assign.target.kind} $value")
// // TODO maybe we can do the typecast on the target directly instead of on the stack?
// asmgen.translateExpression(value)
// assignStackValue(assign.target)
// }
// }
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign)
// is FunctionCall -> {
// if (assign.target.kind == TargetStorageKind.STACK) {
// asmgen.translateExpression(value)
@ -161,6 +151,52 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) {
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
when(value) {
is IdentifierReference -> {
if (valueDt == DataType.UBYTE || valueDt == DataType.BYTE) {
if(targetDt in WordDatatypes) {
assignVariableByteIntoWord(target, value, valueDt)
return
}
}
}
is DirectMemoryRead -> {
if(targetDt in WordDatatypes) {
if (value.addressExpression is NumericLiteralValue) {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
assignMemoryByteIntoWord(target, address, null)
return
}
else if (value.addressExpression is IdentifierReference) {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
return
}
}
}
is NumericLiteralValue -> throw AssemblyError("a cast of a literal value should have been const-folded away")
else -> {}
}
when(value) {
is PrefixExpression -> {}
is BinaryExpression -> {}
is ArrayIndexedExpression -> {}
is TypecastExpression -> {}
is RangeExpr -> {}
is FunctionCall -> {}
else -> {
// TODO optimize the others further?
println("warning: slow stack evaluation used for typecast: into $targetDt at ${value.position}")
}
}
// give up, do it via eval stack
asmgen.translateExpression(origAssign.source.expression!!)
assignStackValue(target)
}
private fun assignStackValue(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@ -520,6 +556,59 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference, valueDt: DataType) {
if(valueDt == DataType.BYTE)
TODO("sign extend byte to word")
val sourceName = asmgen.asmVariableName(bytevar)
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
}
TargetStorageKind.ARRAY -> {
val index = wordtarget.array!!.arrayspec.index
when {
wordtarget.constArrayIndexValue!=null -> {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
index is IdentifierReference -> {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
}
else -> {
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
asmgen.translateExpression(index)
asmgen.out(" inx | lda P8ESTACK_LO,x")
popAndWriteArrayvalueWithUnscaledIndexA(wordtarget.datatype, wordtarget.asmVarname)
}
}
}
TargetStorageKind.REGISTER -> {
when(wordtarget.register!!) {
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
else -> throw AssemblyError("only reg pairs are words")
}
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda #$sourceName
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
}
else -> throw AssemblyError("other types aren't word")
}
}
private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
require(target.datatype in ByteDatatypes)
when(target.kind) {
@ -900,6 +989,63 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: Int?, identifier: IdentifierReference?) {
if (address != null) {
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${address.toHex()}
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
}
TargetStorageKind.REGISTER -> when(wordtarget.register!!) {
RegisterOrPair.AX -> asmgen.out(" lda ${address.toHex()} | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda ${address.toHex()} | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldy ${address.toHex()} | ldy #0")
else -> throw AssemblyError("word regs can only be pair")
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda ${address.toHex()}
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
}
else -> throw AssemblyError("other types aren't word")
}
} else if (identifier != null) {
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta ${wordtarget.asmVarname} | lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
}
TargetStorageKind.REGISTER -> {
asmgen.loadByteFromPointerIntoA(identifier)
when(wordtarget.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx #0")
RegisterOrPair.AY -> asmgen.out(" ldy #0")
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
else -> throw AssemblyError("word regs can only be pair")
}
}
TargetStorageKind.STACK -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("other types aren't word")
}
}
}
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue

View File

@ -218,7 +218,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
// TODO doesn't always remove casts for instance uword xx = xb generates complex stack based type cast evaluation...
if (target.datatype == value.type) {
val childDt = value.expression.inferType(program).typeOrElse(DataType.STRUCT)
if (value.type.equalsSize(childDt) || value.type.largerThan(childDt)) {
@ -1315,7 +1314,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {
val outerCastDt = cast.type
val innerCastDt = (cast.expression as? TypecastExpression)?.type
if (innerCastDt == null) {
// simple typecast where the value is the target
when (target.datatype) {

View File

@ -3,7 +3,7 @@ TODO
====
- get rid of all other TODO's in the code ;-)
- gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
- compiler errors and warnings in standard format so the IDE shows them as clickable links; ./test.asm:2578:3: blablabla
- further optimize assignment codegeneration
- auto select correct library to import based on target, instead of having c64- and cx16- prefix variants

View File

@ -9,24 +9,13 @@ main {
sub start() {
ubyte b
if b > 15 {
b = 99
} else {
; nothing
}
ubyte ub =9
uword yy = 9999 ; this is okay (no 0-initialization generated) but... the next:
uword xx = ub ; TODO don't generate xx = 0 assignment if it's initialized with something else...
if b > 15 {
; nothing
} else {
b = 99
}
if b > 15 {
; nothing
} else {
; nothing
}
ub++
xx++
yy++
;asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { ...}
; TODO dont cause name conflict if we define sub or sub with param 'color' or even a var 'color' later.