From dba08468660ee0d06da0a1bb40f130156906f59a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 27 Feb 2024 21:34:58 +0100 Subject: [PATCH] optimize word >, word <= --- .../src/prog8/codegen/cpu6502/IfElseAsmGen.kt | 416 +++++------------- .../astprocessing/BeforeAsmAstChanger.kt | 78 ++-- compiler/test/TestTypecasts.kt | 20 +- docs/source/todo.rst | 27 +- examples/test.p8 | 18 +- 5 files changed, 183 insertions(+), 376 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt index 3a875ce20..433955999 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -435,8 +435,8 @@ internal class IfElseAsmGen(private val program: PtProgram, "==" -> wordEqualsValue(condition.left, condition.right, false, signed, jumpAfterIf, stmt) "!=" -> wordEqualsValue(condition.left, condition.right, true, signed, jumpAfterIf, stmt) "<" -> wordLessValue(condition.left, condition.right, signed, jumpAfterIf, stmt) - "<=" -> wordLessEqualsValue(condition.left, condition.right, signed, jumpAfterIf, stmt) - ">" -> wordGreaterValue(condition.left, condition.right, signed, jumpAfterIf, stmt) + "<=" -> throw AssemblyError("X<=Y should have been replaced by Y>=X") + ">" -> throw AssemblyError("X>Y should have been replaced by Y=" -> wordGreaterEqualsValue(condition.left, condition.right, signed, jumpAfterIf, stmt) else -> fallbackTranslate(stmt, false) } @@ -591,301 +591,6 @@ _jump jmp ($asmLabel) code("P8ZP_SCRATCH_W2", "P8ZP_SCRATCH_W2+1") } - private fun wordGreaterValue(left: PtExpression, right: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) { - fun code(valueLsb: String, valueMsb: String) { - if(signed) { - // word > X - if(jump!=null) { - val (asmLabel, indirect) = asmgen.getJumpTarget(jump) - if(indirect) { - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bpl + - jmp ($asmLabel) -+""") - } else { - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bmi $asmLabel""") - } - } else { - val afterIfLabel = asmgen.makeLabel("afterif") - if(stmt.hasElse()) { - // if and else blocks - val elseLabel = asmgen.makeLabel("else") - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bpl $elseLabel""") - asmgen.translate(stmt.ifScope) - asmgen.jmp(afterIfLabel, false) - asmgen.out(elseLabel) - asmgen.translate(stmt.elseScope) - } else { - // no else block - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bpl $afterIfLabel""") - asmgen.translate(stmt.ifScope) - } - asmgen.out(afterIfLabel) - } - } else { - // uword > X - if(jump!=null) { - val (asmLabel, indirect) = asmgen.getJumpTarget(jump) - if(indirect) { - asmgen.out(""" - cpy $valueMsb - beq + - bcc ++ - jmp ($asmLabel) -+ cmp $valueLsb - bcc + - beq + - jmp ($asmLabel) -+""") - } else { - asmgen.out(""" - cpy $valueMsb - beq + - bcc ++ - bcs $asmLabel -+ cmp $valueLsb - bcc + - beq + - bne $asmLabel -+""") - } - } else { - val afterIfLabel = asmgen.makeLabel("afterif") - if(stmt.hasElse()) { - val elseLabel = asmgen.makeLabel("else") - asmgen.out(""" - cpy $valueMsb - bcc $elseLabel - bne + - cmp $valueLsb - bcc $elseLabel - beq $elseLabel -+""") - asmgen.translate(stmt.ifScope) - asmgen.jmp(afterIfLabel, false) - asmgen.out(elseLabel) - asmgen.translate(stmt.elseScope) - } else { - // no else block - asmgen.out(""" - cpy $valueMsb - bcc $afterIfLabel - bne + - cmp $valueLsb - bcc $afterIfLabel - beq $afterIfLabel -+""") - asmgen.translate(stmt.ifScope) - } - asmgen.out(afterIfLabel) - } - } - } - - if(right is PtNumber) { - asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, signed) - val number = right.number.toHex() - return code("#<$number", "#>$number") - } - - if(right is PtIdentifier) { - asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, signed) - val variable = asmgen.asmVariableName(right) - return code(variable, variable+"+1") - } - - // TODO optimize for simple array value - - // generic case via scratch register - asmgen.assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2") - code("P8ZP_SCRATCH_W2", "P8ZP_SCRATCH_W2+1") - } - - private fun wordLessEqualsValue(left: PtExpression, right: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) { - fun code(valueLsb: String, valueMsb: String) { - if(signed) { - // word <= X - if(jump!=null) { - val (asmLabel, indirect) = asmgen.getJumpTarget(jump) - if(indirect) { - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bmi + - jmp ($asmLabel) -+""") - } else { - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bpl $asmLabel""") - } - } else { - val afterIfLabel = asmgen.makeLabel("afterif") - if(stmt.hasElse()) { - // if and else blocks - val elseLabel = asmgen.makeLabel("else") - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bmi $elseLabel""") - asmgen.translate(stmt.ifScope) - asmgen.jmp(afterIfLabel, false) - asmgen.out(elseLabel) - asmgen.translate(stmt.elseScope) - } else { - // no else block - asmgen.out(""" - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - ldy $valueLsb - lda $valueMsb - cpy P8ZP_SCRATCH_W1 - sbc P8ZP_SCRATCH_W1+1 - bvc + - eor #128 -+ bmi $afterIfLabel""") - asmgen.translate(stmt.ifScope) - } - asmgen.out(afterIfLabel) - } - } else { - // uword <= X - if(jump!=null) { - val (asmLabel, indirect) = asmgen.getJumpTarget(jump) - if(indirect) { - asmgen.out(""" - sec - sbc $valueLsb - sta P8ZP_SCRATCH_REG - tya - sbc $valueMsb - ora P8ZP_SCRATCH_REG - beq + - bcs ++ -+ jmp ($asmLabel) -+""") - } else { - asmgen.out(""" - sec - sbc $valueLsb - sta P8ZP_SCRATCH_REG - tya - sbc $valueMsb - ora P8ZP_SCRATCH_REG - beq $asmLabel - bcc $asmLabel""") - } - } else { - val afterIfLabel = asmgen.makeLabel("afterif") - if(stmt.hasElse()) { - // if and else blocks - val elseLabel = asmgen.makeLabel("else") - asmgen.out(""" - sec - sbc $valueLsb - sta P8ZP_SCRATCH_REG - tya - sbc $valueMsb - ora P8ZP_SCRATCH_REG - beq + - bcs $elseLabel -+""") - asmgen.translate(stmt.ifScope) - asmgen.jmp(afterIfLabel, false) - asmgen.out(elseLabel) - asmgen.translate(stmt.elseScope) - } else { - // no else block - asmgen.out(""" - sec - sbc $valueLsb - sta P8ZP_SCRATCH_REG - tya - sbc $valueMsb - ora P8ZP_SCRATCH_REG - beq + - bcs $afterIfLabel -+""") - asmgen.translate(stmt.ifScope) - } - asmgen.out(afterIfLabel) - } - } - } - - if(right is PtNumber) { - asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, signed) - val number = right.number.toHex() - return code("#<$number", "#>$number") - } - - if(right is PtIdentifier) { - asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, signed) - val variable = asmgen.asmVariableName(right) - return code(variable, variable+"+1") - } - - // TODO optimize for simple array value - - // generic case via scratch register - asmgen.assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2") - code("P8ZP_SCRATCH_W2", "P8ZP_SCRATCH_W2+1") - } - private fun wordGreaterEqualsValue(left: PtExpression, right: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) { fun code(valueLsb: String, valueMsb: String) { @@ -1037,7 +742,64 @@ _jump jmp ($asmLabel) private fun wordLessEqualsZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) { return if(signed) { // word <= 0 - asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) // TODO optimize this even further by doing MSB/LSB separately + + if(value is PtIdentifier) { + // special optimization to compare msb/lsb separately + // TODO also do this for array? + if(jump!=null) { + val (asmLabel, indirect) = asmgen.getJumpTarget(jump) + if(indirect) { + asmgen.out(""" + lda ${value.name}+1 + bmi + + bne ++ + lda ${value.name} + bne ++ ++ jmp ($asmLabel) ++""") + } else { + asmgen.out(""" + lda ${value.name}+1 + bmi $asmLabel + bne + + lda ${value.name} + beq $asmLabel ++""") + } + asmgen.translate(stmt.elseScope) + } else { + val afterIfLabel = asmgen.makeLabel("afterif") + if(stmt.hasElse()) { + // if and else blocks + val elseLabel = asmgen.makeLabel("else") + asmgen.out(""" + lda ${value.name}+1 + bmi + + bne $elseLabel + lda ${value.name} + bne $elseLabel ++""") + asmgen.translate(stmt.ifScope) + asmgen.jmp(afterIfLabel, false) + asmgen.out(elseLabel) + asmgen.translate(stmt.elseScope) + } else { + // no else block + asmgen.out(""" + lda ${value.name}+1 + bmi + + bne $afterIfLabel + lda ${value.name} + bne $afterIfLabel ++""") + asmgen.translate(stmt.ifScope) + } + asmgen.out(afterIfLabel) + } + return + } + + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) if(jump!=null) { val (asmLabel, indirect) = asmgen.getJumpTarget(jump) if(indirect) { @@ -1097,7 +859,63 @@ _jump jmp ($asmLabel) private fun wordGreaterZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) { if(signed) { // word > 0 - asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) // TODO optimize this even further by doing MSB/LSB separately + if(value is PtIdentifier) { + // special optimization to compare msb/lsb separately + // TODO also do this for array? + if(jump!=null) { + val (asmLabel, indirect) = asmgen.getJumpTarget(jump) + if(indirect) { + asmgen.out(""" + lda ${value.name}+1 + bmi ++ + bne + + lda ${value.name} + beq ++ ++ jmp ($asmLabel) ++""") + } else { + asmgen.out(""" + lda ${value.name}+1 + bmi + + bne $asmLabel + lda ${value.name} + bne $asmLabel ++""") + } + asmgen.translate(stmt.elseScope) + } else { + val afterIfLabel = asmgen.makeLabel("afterif") + if(stmt.hasElse()) { + // if and else blocks + val elseLabel = asmgen.makeLabel("else") + asmgen.out(""" + lda ${value.name}+1 + bmi $elseLabel + bne + + lda ${value.name} + beq $elseLabel ++""") + asmgen.translate(stmt.ifScope) + asmgen.jmp(afterIfLabel, false) + asmgen.out(elseLabel) + asmgen.translate(stmt.elseScope) + } else { + // no else block + asmgen.out(""" + lda ${value.name}+1 + bmi $afterIfLabel + bne + + lda ${value.name} + beq $afterIfLabel ++""") + asmgen.translate(stmt.ifScope) + } + asmgen.out(afterIfLabel) + } + return + } + + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) if(jump!=null) { val (asmLabel, indirect) = asmgen.getJumpTarget(jump) if(indirect) { diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index b090a0845..6bc795d83 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -103,8 +103,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co } override fun after(ifElse: IfElse, parent: Node): Iterable { - val binExpr = ifElse.condition as? BinaryExpression - if(binExpr!=null) { + val binExpr = ifElse.condition as? BinaryExpression ?: return noModifications if(binExpr.operator !in ComparisonOperators) { val constRight = binExpr.right.constValue(program) if(constRight!=null) { @@ -123,40 +122,61 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co } } - if((binExpr.left as? NumericLiteral)?.number==0.0 && + if(binExpr.operator=="==" && + (binExpr.left as? NumericLiteral)?.number==0.0 && (binExpr.right as? NumericLiteral)?.number!=0.0) - throw InternalCompilerException("0==X should be just X") - - } + throw InternalCompilerException("0==X should be just X ${binExpr.position}") return noModifications } override fun after(expr: BinaryExpression, parent: Node): Iterable { - if(options.compTarget.name!=VMTarget.NAME) { - val rightNum = expr.right.constValue(program) - if(rightNum!=null && rightNum.type in IntegerDatatypes) { - //val signed = expr.left.inferType(program).getOr(DataType.UNDEFINED) in SignedDatatypes - when(expr.operator) { - ">" -> { - // X>N -> X>=N+1, easier to do in 6502 - // TODO check if useful for words as well - val maximum = if(rightNum.type in ByteDatatypes) 255 else 65535 - if(rightNum.number=", NumericLiteral(rightNum.type, numPlusOne.toDouble(), rightNum.position), expr.position) - return listOf(IAstModification.ReplaceNode(expr, newExpr, parent)) - } + if (options.compTarget.name == VMTarget.NAME) + return noModifications + + val rightDt = expr.right.inferType(program) + val rightNum = expr.right.constValue(program) + + if(rightDt.isWords && (rightNum==null || rightNum.number!=0.0)) { + when (expr.operator) { + ">" -> { + // X>Y -> Y=" + val left = expr.left + expr.left = expr.right + expr.right = left + return noModifications + } + } + } + + if(rightNum!=null && rightNum.type in IntegerDatatypes && rightNum.number!=0.0) { + when(expr.operator) { + ">" -> { + // X>N -> X>=N+1, easier to do in 6502 + val maximum = if(rightNum.type in ByteDatatypes) 255 else 65535 + if(rightNum.number=", NumericLiteral(rightNum.type, numPlusOne.toDouble(), rightNum.position), expr.position) + return listOf(IAstModification.ReplaceNode(expr, newExpr, parent)) } - "<=" -> { - // X<=N -> X0 { bb++ @@ -413,11 +413,11 @@ main { val text = """ main { sub start() { - byte bb - word ww + byte @shared bb + word @shared ww - bool iteration_in_progress - uword num_bytes + bool @shared iteration_in_progress + uword @shared num_bytes if not iteration_in_progress or num_bytes==0 { num_bytes++ @@ -455,10 +455,10 @@ main { val text = """ main { sub start() { - byte ub1 = -50 - byte ub2 = -51 - byte ub3 = -52 - byte ub4 = 100 + byte @shared ub1 = -50 + byte @shared ub2 = -51 + byte @shared ub3 = -52 + byte @shared ub4 = 100 word @shared ww = func(ub1, ub2, ub3, ub4) ww = func(ub4, ub2, ub3, ub1) ww=afunc(ub1, ub2, ub3, ub4) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 8cb050db6..386fc5acb 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,33 +1,16 @@ TODO ==== -optimize signed word wordGreaterValue, wordLessEqualsValue (can it be without scratch vars?) -optimize signed word wordGreaterZero, wordLessEqualsZero (by comparing msb/lsb separately?) +floatparse is a lot larger +snow is a lot larger +neofetch is larger +chess is larger + -verify ifelse codegens to be shortest code: (some are using too many scratch vars?) -uword >= -uword > -uword <= -uword < -word >= -word > -word <= -word < explore possible optimizations for words when comparing to a constant number (BeforeAsmAstChanger) -floatparse is a bit larger -testgfx2 is a quite a bit larger -amiga is a bit larger -halloween is 1 byte larger -rockrunner is bigger than on 10.1 -paint is slightly bigger than on 10.1 -chess is bigger than on 10.1 -imageviewer is a lot bigger -shell is a couple of bytes bigger - - add tests for comparison that do an assignment rather than an if assign to variable, and barray[indexvar], test if they're both correct (bb = value > -100 --> contains a beq +++ that shouldn't be there??) diff --git a/examples/test.p8 b/examples/test.p8 index a3e0d6deb..0edf75521 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,24 +5,10 @@ main { sub start() { - if cx16.r0sL > 10 + if cx16.r0s > 0 cx16.r1L++ - if cx16.r0sL >= 10 + if cx16.r0s <= 0 cx16.r1L++ - if cx16.r0sL < 10 - cx16.r1L++ - if cx16.r0sL <= 10 - cx16.r1L++ - } - - sub ub() -> ubyte { - cx16.r0++ - return cx16.r0L - } - - sub sb() -> byte { - cx16.r0++ - return cx16.r0sL } }