diff --git a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt index b48b602bb..3fdc75770 100644 --- a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt +++ b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt @@ -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) diff --git a/codeOptimizers/src/prog8/optimizer/Extensions.kt b/codeOptimizers/src/prog8/optimizer/Extensions.kt index 13bd57aec..7a37b4eb1 100644 --- a/codeOptimizers/src/prog8/optimizer/Extensions.kt +++ b/codeOptimizers/src/prog8/optimizer/Extensions.kt @@ -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 { + 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") + } +} diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index 81654294c..088d4186c 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -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( diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index e95c6dda2..47eed1824 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -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), diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 27a01b364..f00880bf7 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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 - ... diff --git a/examples/cx16/cube3d.p8 b/examples/cx16/cube3d.p8 index 149e0e7fb..023798b17 100644 --- a/examples/cx16/cube3d.p8 +++ b/examples/cx16/cube3d.p8 @@ -25,6 +25,8 @@ main { anglex+=500 angley+=215 anglez+=453 + sys.waitvsync() + sys.waitvsync() } } diff --git a/examples/test.p8 b/examples/test.p8 index 1f72d9489..abc4d6f1a 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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()