split some additional binary expressions to avoid stack-based evaluation

This commit is contained in:
Irmen de Jong 2021-11-28 18:27:28 +01:00
parent 47c2c0376a
commit 32068a832a
7 changed files with 48 additions and 32 deletions

View File

@ -46,8 +46,32 @@ X = BinExpr X = LeftExpr
*/
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
if(assignment.target isSameAs binExpr.right)
return noModifications
if(assignment.target isSameAs binExpr.left) {
if(binExpr.right.isSimple)
return noModifications
val leftBx = binExpr.left as? BinaryExpression
if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple))
return noModifications
val rightBx = binExpr.right as? BinaryExpression
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
return noModifications
// TODO below attempts to remove stack-based evaluated expressions, but sometimes the resulting code is BIGGER.
val dt = assignment.target.inferType(program)
if(!dt.isInteger)
return noModifications
val tempVar = IdentifierReference(getTempVarName(dt), binExpr.right.position)
val assignTempVar = Assignment(
AssignTarget(tempVar, null, null, binExpr.right.position),
binExpr.right, binExpr.right.position
)
return listOf(
IAstModification.InsertBefore(assignment, assignTempVar, assignment.parent as IStatementContainer),
IAstModification.ReplaceNode(binExpr.right, tempVar.copy(), binExpr)
)
}
if(binExpr.right.isSimple) {
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, binExpr.left.position)

View File

@ -2,6 +2,9 @@ package prog8.optimizer
import prog8.ast.IBuiltinFunctions
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.InferredTypes
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
@ -65,3 +68,15 @@ fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICom
opti.visit(this)
return opti.applyModifications()
}
fun getTempVarName(dt: InferredTypes.InferredType): List<String> {
return when {
// TODO assume (hope) cx16.r9 isn't used for anything else...
dt.istype(DataType.UBYTE) -> listOf("cx16", "r9L")
dt.istype(DataType.BYTE) -> listOf("cx16", "r9sL")
dt.istype(DataType.UWORD) -> listOf("cx16", "r9")
dt.istype(DataType.WORD) -> listOf("cx16", "r9s")
dt.isPassByReference -> listOf("cx16", "r9")
else -> throw FatalAstException("invalid dt $dt")
}
}

View File

@ -120,16 +120,7 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
val arg = functionCallStatement.args[0]
if(!arg.isSimple && arg !is TypecastExpression && arg !is IFunctionCall) {
val dt = arg.inferType(program)
val name = when {
// TODO assume (hope) cx16.r9 isn't used for anything else...
dt.istype(DataType.UBYTE) -> listOf("cx16","r9L")
dt.istype(DataType.BYTE) -> listOf("cx16","r9sL")
dt.istype(DataType.UWORD) -> listOf("cx16","r9")
dt.istype(DataType.WORD) -> listOf("cx16","r9s")
dt.isPassByReference -> listOf("cx16","r9")
else -> throw FatalAstException("invalid dt $dt")
}
val name = getTempVarName(arg.inferType(program))
val tempvar = IdentifierReference(name, functionCallStatement.position)
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, functionCallStatement.position)
return listOf(

View File

@ -13,6 +13,7 @@ import prog8.ast.walk.IAstVisitor
import prog8.compiler.astprocessing.isSubroutineParameter
import prog8.compiler.target.AssemblyError
import prog8.compilerinterface.*
import prog8.optimizer.getTempVarName
internal class BeforeAsmGenerationAstChanger(val program: Program, private val options: CompilationOptions,
@ -228,16 +229,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall
if(separateLeftExpr) {
val dt = expr.left.inferType(program)
val name = when {
// TODO assume (hope) cx16.r9 isn't used for anything else...
dt.istype(DataType.UBYTE) -> listOf("cx16","r9L")
dt.istype(DataType.BYTE) -> listOf("cx16","r9sL")
dt.istype(DataType.UWORD) -> listOf("cx16","r9")
dt.istype(DataType.WORD) -> listOf("cx16","r9s")
dt.isPassByReference -> listOf("cx16","r9")
else -> throw AssemblyError("invalid dt")
}
val name = getTempVarName(expr.left.inferType(program))
leftOperandReplacement = IdentifierReference(name, expr.position)
leftAssignment = Assignment(
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),

View File

@ -8,14 +8,11 @@ Use GoSub to call subroutines (statements):
- [DONE] turn a regular subroutine call into assignments to the parameters + GoSub (take code from gosub branch)
- [DONE] also do this for asmsubs taking >0 parameters
- make that push(x+1) doesn't use stack evaluation, via a temp var cx16.R9?
Optimize Function calls in expressions:
- move args to assignments to params
- add tempvar immediately in front of expression with the fuction call
- replace the function call in the expression with the tempvar
...

View File

@ -25,6 +25,8 @@ main {
anglex+=500
angley+=215
anglez+=453
sys.waitvsync()
sys.waitvsync()
}
}

View File

@ -6,14 +6,9 @@ main {
sub start() {
test_stack.test()
ubyte x1 = 10
ubyte x2 = 20
ubyte x3 = 30
x1 += x2+x3 ; TODO WHY SLOW EVAL????
x1 += x2-x3 ; TODO WHY SLOW EVAL????
txt.print_ub(x1)
ubyte @shared x1 = 10
ubyte @shared x2 = 20
ubyte @shared x3 = 30
test_stack.test()