mirror of
https://github.com/irmen/prog8.git
synced 2024-10-18 01:24:51 +00:00
removed problematic expression "simplifications" (that introduced arbitrary r9 temp register usage)
This commit is contained in:
parent
f718f4251b
commit
93ce74eeb1
@ -1,6 +1,7 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
@ -81,26 +82,6 @@ class StatementOptimizer(private val program: Program,
|
|||||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(compTarget.name!=VMTarget.NAME) {
|
|
||||||
// see if we can optimize any complex argument expressions to be just a simple variable
|
|
||||||
// TODO for now, only works for single-argument functions because we use just 1 temp var: R9
|
|
||||||
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
|
|
||||||
val arg = functionCallStatement.args[0]
|
|
||||||
if(!arg.isSimple && arg !is IFunctionCall) {
|
|
||||||
val dt = arg.inferType(program)
|
|
||||||
if(dt.isInteger) {
|
|
||||||
val name = getTempRegisterName(dt)
|
|
||||||
val tempvar = IdentifierReference(name, functionCallStatement.position)
|
|
||||||
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.InsertBefore(functionCallStatement, assignTempvar, parent as IStatementContainer),
|
|
||||||
IAstModification.ReplaceNode(arg, tempvar, functionCallStatement)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,32 +394,21 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(compTarget.name==VMTarget.NAME)
|
if(compTarget.name==VMTarget.NAME)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
val returnvalue = returnStmt.value
|
||||||
val subr = returnStmt.definingSubroutine!!
|
if (returnvalue!=null) {
|
||||||
val returnDt = subr.returntypes.single()
|
val dt = returnvalue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||||
if (returnDt in IntegerDatatypes) {
|
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
|
||||||
// first assign to intermediary variable, then return that
|
// first assign to intermediary variable, then return that
|
||||||
val (returnVarName, _) = program.getTempVar(returnDt)
|
val (returnVarName, _) = program.getTempVar(dt)
|
||||||
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
|
||||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
||||||
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
val assign = Assignment(tgt, returnvalue, AssignmentOrigin.OPTIMIZER, returnStmt.position)
|
||||||
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO decision when to use intermediary variable to calculate returnvalue seems a bit arbitrary...
|
|
||||||
val returnvalue = returnStmt.value
|
|
||||||
if (returnvalue!=null) {
|
|
||||||
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
|
|
||||||
val mod = returnViaIntermediaryVar(returnvalue)
|
|
||||||
if(mod!=null)
|
|
||||||
return mod
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
|
@ -166,7 +166,6 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
// (non-asm routines get a Return statement as needed, above)
|
// (non-asm routines get a Return statement as needed, above)
|
||||||
val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n"
|
val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n"
|
||||||
mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine)
|
mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine)
|
||||||
println("adding returnstmt3 ${subroutine.hasRtsInAsm(options.compTarget)}") // TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,88 +216,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
(binExpr.right as? NumericLiteral)?.number!=0.0)
|
(binExpr.right as? NumericLiteral)?.number!=0.0)
|
||||||
throw InternalCompilerException("0==X should have been swapped to if X==0")
|
throw InternalCompilerException("0==X should have been swapped to if X==0")
|
||||||
|
|
||||||
// simplify the conditional expression, introduce simple assignments if required.
|
return noModifications
|
||||||
// NOTE: sometimes this increases code size because additional stores/loads are generated for the
|
|
||||||
// intermediate variables. We assume these are optimized away from the resulting assembly code later.
|
|
||||||
val simplify = simplifyConditionalExpression(binExpr)
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
if(simplify.rightVarAssignment!=null) {
|
|
||||||
modifications += IAstModification.ReplaceNode(binExpr.right, simplify.rightOperandReplacement!!, binExpr)
|
|
||||||
modifications += IAstModification.InsertBefore(
|
|
||||||
ifElse,
|
|
||||||
simplify.rightVarAssignment,
|
|
||||||
parent as IStatementContainer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if(simplify.leftVarAssignment!=null) {
|
|
||||||
modifications += IAstModification.ReplaceNode(binExpr.left, simplify.leftOperandReplacement!!, binExpr)
|
|
||||||
modifications += IAstModification.InsertBefore(
|
|
||||||
ifElse,
|
|
||||||
simplify.leftVarAssignment,
|
|
||||||
parent as IStatementContainer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CondExprSimplificationResult(
|
|
||||||
val leftVarAssignment: Assignment?,
|
|
||||||
val leftOperandReplacement: Expression?,
|
|
||||||
val rightVarAssignment: Assignment?,
|
|
||||||
val rightOperandReplacement: Expression?
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun simplifyConditionalExpression(expr: BinaryExpression): CondExprSimplificationResult {
|
|
||||||
|
|
||||||
// TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off,
|
|
||||||
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
|
|
||||||
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
|
|
||||||
|
|
||||||
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
|
|
||||||
return CondExprSimplificationResult(null, null, null, null)
|
|
||||||
|
|
||||||
var leftAssignment: Assignment? = null
|
|
||||||
var leftOperandReplacement: Expression? = null
|
|
||||||
var rightAssignment: Assignment? = null
|
|
||||||
var rightOperandReplacement: Expression? = null
|
|
||||||
|
|
||||||
val separateLeftExpr = !expr.left.isSimple
|
|
||||||
&& expr.left !is IFunctionCall
|
|
||||||
&& expr.left !is ContainmentCheck
|
|
||||||
val separateRightExpr = !expr.right.isSimple
|
|
||||||
&& expr.right !is IFunctionCall
|
|
||||||
&& expr.right !is ContainmentCheck
|
|
||||||
val leftDt = expr.left.inferType(program)
|
|
||||||
val rightDt = expr.right.inferType(program)
|
|
||||||
|
|
||||||
if(!leftDt.isInteger || !rightDt.isInteger) {
|
|
||||||
// we can't reasonably simplify non-integer expressions
|
|
||||||
return CondExprSimplificationResult(null, null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(separateLeftExpr) {
|
|
||||||
val name = getTempRegisterName(leftDt)
|
|
||||||
leftOperandReplacement = IdentifierReference(name, expr.position)
|
|
||||||
leftAssignment = Assignment(
|
|
||||||
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
|
|
||||||
expr.left,
|
|
||||||
AssignmentOrigin.BEFOREASMGEN, expr.position
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if(separateRightExpr) {
|
|
||||||
val (tempVarName, _) = program.getTempVar(rightDt.getOrElse { throw FatalAstException("invalid dt") }, true)
|
|
||||||
rightOperandReplacement = IdentifierReference(tempVarName, expr.position)
|
|
||||||
rightAssignment = Assignment(
|
|
||||||
AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position),
|
|
||||||
expr.right,
|
|
||||||
AssignmentOrigin.BEFOREASMGEN, expr.position
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return CondExprSimplificationResult(
|
|
||||||
leftAssignment, leftOperandReplacement,
|
|
||||||
rightAssignment, rightOperandReplacement
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
@ -827,7 +827,7 @@ main {
|
|||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.program.entrypoint.statements
|
||||||
statements.size shouldBe 27
|
statements.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
test("cast to unsigned in conditional") {
|
test("cast to unsigned in conditional") {
|
||||||
|
@ -3,7 +3,6 @@ package prog8.ast
|
|||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.InferredTypes
|
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.ast.statements.VarDeclOrigin
|
import prog8.ast.statements.VarDeclOrigin
|
||||||
import prog8.ast.statements.VarDeclType
|
import prog8.ast.statements.VarDeclType
|
||||||
@ -48,19 +47,6 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>
|
|||||||
return Pair(tmpvarName, decl)
|
return Pair(tmpvarName, decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTempRegisterName(dt: InferredTypes.InferredType): List<String> {
|
|
||||||
return when {
|
|
||||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
|
|
||||||
dt istype DataType.UBYTE -> listOf("cx16", "r9L")
|
|
||||||
dt istype DataType.BOOL -> 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun maySwapOperandOrder(binexpr: BinaryExpression): Boolean {
|
fun maySwapOperandOrder(binexpr: BinaryExpression): Boolean {
|
||||||
fun ok(expr: Expression): Boolean {
|
fun ok(expr: Expression): Boolean {
|
||||||
return when(expr) {
|
return when(expr) {
|
||||||
|
@ -3,6 +3,8 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
- fix the obvious TODOs in CodeGen (vm)
|
||||||
|
- vm: AssignmentGen - fix in-place array assign TODO
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user