reducing expression codegen complexity (no longer splitting conditional expressions, and using r9 as temp var)

This commit is contained in:
Irmen de Jong 2023-09-12 22:11:10 +02:00
parent 6afdd4e6fd
commit 03e0d4b2e8
4 changed files with 20 additions and 160 deletions

View File

@ -1540,7 +1540,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
// TODO optimized code for (word1 & word2) == 0 :
// if(expr.left.type in WordDatatypes && (expr.operator=="==" || expr.operator=="!=") && expr.left is PtBinaryExpression) {
// ...
// println("TODO optimize logical word expr ${expr.position}")
// }
when (expr.operator) {

View File

@ -1,16 +1,17 @@
package prog8.compiler.astprocessing
import prog8.ast.*
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
import prog8.ast.statements.IfElse
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.code.core.DataType
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.IntegerDatatypes
internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
@ -99,108 +100,4 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
}
return noModifications
}
override fun before(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
return noModifications
val binExpr = ifElse.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in ComparisonOperators)
return noModifications
// Simplify the conditional expression, introduce simple assignments if required.
// 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 {
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
return CondExprSimplificationResult(null, null, null, null)
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)
}
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
if(separateLeftExpr) {
val name = getTempRegisterName(leftDt)
leftOperandReplacement = IdentifierReference(name, expr.position)
leftAssignment = Assignment(
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
expr.left.copy(),
AssignmentOrigin.ASMGEN, 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.copy(),
AssignmentOrigin.ASMGEN, expr.position
)
}
return CondExprSimplificationResult(
leftAssignment, leftOperandReplacement,
rightAssignment, rightOperandReplacement
)
}
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")
}
}
}

View File

@ -1,7 +1,9 @@
TODO
====
- optimize assembly output for (word1 & word2 ==0) ... (no need for stack pushes) see attemptAssignToByteCompareZero().
- possibly optimize assembly output for (word1 & word2 ==0)
see attemptAssignToByteCompareZero() for assignment.
see testZeroOrJumpElsewhere() for if condition.
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction

View File

@ -1,55 +1,16 @@
%import textio
%import floats
%zeropage basicsafe
; TODO fix VM : produces wrong number of primes (and varies too, so it uses uninitialized memory somewhere)
main {
const uword SIZE = 16000
uword @zp flags_ptr = memory("flags", SIZE/8+1, $100)
ubyte[] bitv = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
uword w1
uword w2
sub start() {
txt.print("calculating... (expecting 3431): ")
txt.print_uw(sieve())
txt.print(" primes\n")
}
bool zz = (w1 & w2)==0
sub check_flag(uword idx) -> ubyte
{
ubyte mask = bitv[lsb(idx)&7]
ubyte flag = flags_ptr[idx/8]
return flag & mask
}
if (w1 & w2)
w1++
sub clear_flag(uword idx)
{
ubyte mask = bitv[lsb(idx)&7]
ubyte flag = flags_ptr[idx/8]
flag &= ~mask
flags_ptr[idx/8] = flag
}
sub sieve() -> uword {
uword prime
uword k
uword count=0
uword i
sys.memset(flags_ptr, SIZE/8+1, $ff)
for i in 0 to SIZE-1 {
if check_flag(i) {
prime = i*2 + 3
k = i + prime
while k < SIZE {
clear_flag(k)
k += prime
}
count++
}
}
return count
if not(w1 & w2)
w1++
}
}