optimized 6502 codegen for logical expressions

This commit is contained in:
Irmen de Jong 2022-06-15 22:16:17 +02:00
parent cc174b7b85
commit bda016bb3b
6 changed files with 71 additions and 18 deletions

View File

@ -2910,6 +2910,9 @@ $repeatLabel lda $counterVar
}
}
internal fun needAsaveForExpr(arg: Expression): Boolean =
arg !is NumericLiteral && arg !is IdentifierReference && (arg !is DirectMemoryRead || !arg.isSimple)
private val subroutineExtrasCache = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {

View File

@ -44,7 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall)
@ -698,7 +698,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun funcBoolean(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
private fun funcBoolean(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
@ -884,7 +884,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = needAsaveForOtherArg(fcall.args[0])
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) {
val mr0 = fcall.args[0] as? DirectMemoryRead
val mr1 = fcall.args[1] as? DirectMemoryRead
@ -1133,7 +1133,4 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun needAsaveForOtherArg(arg: Expression): Boolean =
arg !is NumericLiteral && arg !is IdentifierReference && arg !is DirectMemoryRead
}

View File

@ -69,10 +69,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
private fun needAsaveForOtherArg(arg: Expression): Boolean =
arg !is NumericLiteral && arg !is IdentifierReference && arg !is DirectMemoryRead
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
// Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!
@ -112,10 +108,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} else {
// 2 byte params, second in Y, first in A
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
if(needAsaveForOtherArg(call.args[1]))
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
if(needAsaveForOtherArg(call.args[1]))
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pla")
}
} else {

View File

@ -2,6 +2,7 @@ package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.getTempRegisterName
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
@ -321,6 +322,63 @@ internal class AssignmentAsmGen(private val program: Program,
if(!expr.inferType(program).isInteger)
return false
// optimized code for logical expressions
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
if(expr.operator=="and") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" and $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for and, expected byte $expr @${expr.position}")
}
else if(expr.operator=="or") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" ora $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for or, expected byte $expr @${expr.position}")
}
else if(expr.operator=="xor") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" eor $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for xor, expected byte $expr @${expr.position}")
}
if(expr.operator!="+" && expr.operator!="-")
return false

View File

@ -3,8 +3,6 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- optimize logical expressions in attemptAssignOptimizedBinexpr()
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
...
@ -19,6 +17,7 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
- vm: implement remaining sin/cos functions in math.p8
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
- vm: don't store symbol names in instructions to make optimizing the IR easier? but what about jumps to labels. And it's no longer readable by humans.

View File

@ -73,10 +73,10 @@ main {
ubyte value
uword wvalue
txt.print("short and with false (word): ")
wvalue = funcw() and funcFalseWord() and funcw() and funcw()
txt.print_uw(wvalue)
txt.nl()
; txt.print("short and with false (word): ")
; wvalue = funcw() and funcFalseWord() and funcw() and funcw()
; txt.print_uw(wvalue)
; txt.nl()
txt.print("short and with false: ")
value = func1(25) and funcFalse()