diff --git a/codeCore/src/prog8/code/core/Operators.kt b/codeCore/src/prog8/code/core/Operators.kt index a706e5045..ca4e40abb 100644 --- a/codeCore/src/prog8/code/core/Operators.kt +++ b/codeCore/src/prog8/code/core/Operators.kt @@ -3,7 +3,7 @@ package prog8.code.core val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor") -val LogicalOperators = setOf("and", "or", "xor") // not x is replaced with x==0 +val LogicalOperators = setOf("and", "or", "xor", "not") val BitwiseOperators = setOf("&", "|", "^") fun invertedComparisonOperator(operator: String) = diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index b72d03401..3b3d9c561 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -2,7 +2,6 @@ package prog8.optimizer import prog8.ast.Node import prog8.ast.Program -import prog8.ast.base.ExpressionError import prog8.ast.base.FatalAstException import prog8.ast.base.UndefinedSymbolError import prog8.ast.expressions.* @@ -14,7 +13,6 @@ import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.AssociativeOperators import prog8.code.core.DataType -import prog8.code.core.IntegerDatatypes class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { @@ -36,54 +34,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { } override fun after(expr: PrefixExpression, parent: Node): Iterable { - // Try to turn a unary prefix expression into a single constant value. - // Compile-time constant sub expressions will be evaluated on the spot. - // For instance, the expression for "- 4.5" will be optimized into the float literal -4.5 - val subexpr = expr.expression - if (subexpr is NumericLiteral) { - // accept prefixed literal values (such as -3, not true) - return when (expr.operator) { - "+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent)) - "-" -> when (subexpr.type) { - in IntegerDatatypes -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral.optimalInteger(-subexpr.number.toInt(), subexpr.position), - parent)) - } - DataType.FLOAT -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral(DataType.FLOAT, -subexpr.number, subexpr.position), - parent)) - } - else -> throw ExpressionError("can only take negative of int or float", subexpr.position) - } - "~" -> when (subexpr.type) { - DataType.BYTE -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position), - parent)) - } - DataType.UBYTE -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position), - parent)) - } - DataType.WORD -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position), - parent)) - } - DataType.UWORD -> { - listOf(IAstModification.ReplaceNode(expr, - NumericLiteral(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position), - parent)) - } - else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position) - } - else -> throw ExpressionError(expr.operator, subexpr.position) - } - } - return noModifications + val constValue = expr.constValue(program) ?: return noModifications + return listOf(IAstModification.ReplaceNode(expr, constValue, parent)) } /* diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 44b1deebe..dcdb86e0d 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -330,6 +330,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions errors.report() program.reorderStatements(errors, compilerOptions) errors.report() + program.changeNotExpression(errors) + errors.report() program.addTypecasts(errors, compilerOptions) errors.report() program.variousCleanups(errors, compilerOptions) diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 0d8c14398..6480b12fb 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -44,6 +44,14 @@ internal fun Program.reorderStatements(errors: IErrorReporter, options: Compilat } } +internal fun Program.changeNotExpression(errors: IErrorReporter) { + val changer = NotExpressionChanger(this, errors) + changer.visit(this) + while(errors.noErrors() && changer.applyModifications()>0) { + changer.visit(this) + } +} + internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) { val walker = object : AstWalker() { override fun after(char: CharLiteral, parent: Node): Iterable { diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index f1c008a45..7282efd61 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -3,7 +3,6 @@ package prog8.compiler.astprocessing import prog8.ast.IFunctionCall import prog8.ast.Node import prog8.ast.Program -import prog8.ast.base.FatalAstException import prog8.ast.base.SyntaxError import prog8.ast.expressions.* import prog8.ast.statements.* @@ -114,17 +113,6 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp return noModifications } - override fun before(expr: PrefixExpression, parent: Node): Iterable { - if(expr.operator == "not") { - // not(x) --> x==0 - // this means that "not" will never occur anywhere again in the ast - val dt = expr.expression.inferType(program).getOr(DataType.UBYTE) - val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(dt,0.0, expr.position), expr.position) - return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent)) - } - return noModifications - } - override fun after(decl: VarDecl, parent: Node): Iterable { val nextAssignment = decl.nextSibling() as? Assignment if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) { @@ -164,6 +152,8 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp expr else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators) expr + else if(expr is PrefixExpression && expr.operator in LogicalOperators) + expr else FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index fba3c293f..3fc37769d 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -231,8 +231,12 @@ internal class BeforeAsmAstChanger(val program: Program, 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 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) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 3796f8b77..eac6ac9b3 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -1,7 +1,10 @@ package prog8.compiler.astprocessing import prog8.ast.* -import prog8.ast.expressions.* +import prog8.ast.expressions.BinaryExpression +import prog8.ast.expressions.DirectMemoryRead +import prog8.ast.expressions.FunctionCallExpression +import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification diff --git a/compiler/src/prog8/compiler/astprocessing/NotExpressionChanger.kt b/compiler/src/prog8/compiler/astprocessing/NotExpressionChanger.kt new file mode 100644 index 000000000..7e2f432b8 --- /dev/null +++ b/compiler/src/prog8/compiler/astprocessing/NotExpressionChanger.kt @@ -0,0 +1,113 @@ +package prog8.compiler.astprocessing + +import prog8.ast.Node +import prog8.ast.Program +import prog8.ast.expressions.BinaryExpression +import prog8.ast.expressions.Expression +import prog8.ast.expressions.NumericLiteral +import prog8.ast.expressions.PrefixExpression +import prog8.ast.statements.Assignment +import prog8.ast.walk.AstWalker +import prog8.ast.walk.IAstModification +import prog8.code.core.IErrorReporter +import prog8.code.core.IntegerDatatypes + +internal class NotExpressionChanger(val program: Program, val errors: IErrorReporter) : AstWalker() { + + override fun before(expr: BinaryExpression, parent: Node): Iterable { + if(expr.operator=="==" || expr.operator=="!=") { + val left = expr.left as? BinaryExpression + if (left != null) { + val rightValue = expr.right.constValue(program) + if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypes) { + if (left.operator == "==" && expr.operator == "==") { + // (x==something)==0 --> x!=something + left.operator = "!=" + return listOf(IAstModification.ReplaceNode(expr, left, parent)) + } else if (left.operator == "!=" && expr.operator == "==") { + // (x!=something)==0 --> x==something + left.operator = "==" + return listOf(IAstModification.ReplaceNode(expr, left, parent)) + } else if (left.operator == "==" && expr.operator == "!=") { + // (x==something)!=0 --> x==something + left.operator = "==" + return listOf(IAstModification.ReplaceNode(expr, left, parent)) + } else if (left.operator == "!=" && expr.operator == "!=") { + // (x!=something)!=0 --> x!=something + left.operator = "!=" + return listOf(IAstModification.ReplaceNode(expr, left, parent)) + } + } + } + } + + val left = expr.left as? BinaryExpression + val right = expr.right as? BinaryExpression + val leftValue = left?.right?.constValue(program)?.number + val rightValue = right?.right?.constValue(program)?.number + + if(expr.operator == "or") { + if(left?.operator=="==" && right?.operator=="==" && leftValue==0.0 && rightValue==0.0) { + // (a==0) or (b==0) -> (a and b)==0 + val orExpr = BinaryExpression(left.left, "and", right.left, expr.position) + val equalsZero = BinaryExpression(orExpr, "==", NumericLiteral.fromBoolean(false, expr.position), expr.position) + return listOf(IAstModification.ReplaceNode(expr, equalsZero, parent)) + } + } + else if(expr.operator == "and") { + if(left?.operator=="==" && right?.operator=="==" && leftValue==0.0 && rightValue==0.0) { + // (a==0) and (b==0) -> (a or b)==0 + val orExpr = BinaryExpression(left.left, "or", right.left, expr.position) + val equalsZero = BinaryExpression(orExpr, "==", NumericLiteral.fromBoolean(false, expr.position), expr.position) + return listOf(IAstModification.ReplaceNode(expr, equalsZero, parent)) + } + } + return noModifications + } + + override fun after(expr: BinaryExpression, parent: Node): Iterable { + // not a or not b -> not(a and b) + if(expr.operator=="or") { + val left = expr.left as? PrefixExpression + val right = expr.right as? PrefixExpression + if(left?.operator=="not" && right?.operator=="not") { + val andExpr = BinaryExpression(left.expression, "and", right.expression, expr.position) + val notExpr = PrefixExpression("not", andExpr, expr.position) + return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) + } + } + + // not a and not b -> not(a or b) + if(expr.operator=="and") { + val left = expr.left as? PrefixExpression + val right = expr.right as? PrefixExpression + if(left?.operator=="not" && right?.operator=="not") { + val andExpr = BinaryExpression(left.expression, "or", right.expression, expr.position) + val notExpr = PrefixExpression("not", andExpr, expr.position) + return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) + } + } + + if(expr.operator=="==") { + val rightValue = expr.right.constValue(program) + if(rightValue?.number==0.0 && rightValue.type in IntegerDatatypes) { + // x==0 -> not x (only if occurs as a subexpression) + if(expr.parent is Expression || expr.parent is Assignment) { + val notExpr = PrefixExpression("not", expr.left.copy(), expr.position) + return listOf(IAstModification.ReplaceNodeSafe(expr, notExpr, parent)) + } + } + } + + return noModifications + } + + override fun after(expr: PrefixExpression, parent: Node): Iterable { + if(expr.operator == "not") { + // not(not(x)) -> x + if((expr.expression as? PrefixExpression)?.operator=="not") + return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent)) + } + return noModifications + } +} diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 1d02eeb66..a8e2fc77d 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -306,12 +306,14 @@ internal class StatementReorderer(val program: Program, val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position) val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position) listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) - } else { + } + else if(leftBinExpr.left.constValue(program)!=null && binExpr.right.constValue(program)!=null) { // A = (x A) y ==> A = A (x y) val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position) val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position) listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment)) } + else noModifications } val rightBinExpr = binExpr.right as? BinaryExpression if(rightBinExpr?.operator == binExpr.operator) { diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index f4eca15ac..2eb321c5f 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -48,7 +48,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, if(parent is Assignment) { val targetDt = (parent).target.inferType(program).getOrElse { throw FatalAstException("invalid dt") } if(sourceDt istype targetDt) { - // we can get rid of this typecast because the type is already + // we can get rid of this typecast because the type is already the target type return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) } } @@ -71,6 +71,13 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, // +X --> X return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent)) } + else if(expr.operator == "not") { + // not(x) --> x==0 + // this means that "not" will never occur anywhere again in the ast after this + val dt = expr.expression.inferType(program).getOr(DataType.UBYTE) + val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(dt,0.0, expr.position), expr.position) + return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent)) + } return noModifications } diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 19d173126..d4165a6e3 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -1,12 +1,10 @@ package prog8tests -import io.kotest.assertions.fail import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain -import io.kotest.matchers.string.shouldNotBeBlank import io.kotest.matchers.string.shouldStartWith import io.kotest.matchers.types.instanceOf import io.kotest.matchers.types.shouldBeSameInstanceAs @@ -14,9 +12,9 @@ import prog8.ast.ParentSentinel import prog8.ast.Program import prog8.ast.expressions.* import prog8.ast.statements.* -import prog8.code.core.* +import prog8.code.core.DataType +import prog8.code.core.Position import prog8.code.target.C64Target -import prog8.compiler.astprocessing.processAstBeforeAsmGeneration import prog8.compiler.printProgram import prog8tests.helpers.* @@ -253,83 +251,41 @@ class TestOptimization: FunSpec({ (initY2.value as NumericLiteral).number shouldBe 11.0 } - test("not-typecasted assignment from ubyte logical expression to uword var should be auto upcasted") { + test("various 'not' operator rewrites even without optimizations on") { val src = """ main { sub start() { - ubyte bb - uword ww - ww = not bb or not ww ; expression combining ubyte and uword + ubyte a1 + ubyte a2 + a1 = not not a1 ; a1 = a1==0 + a1 = not a1 or not a2 ; a1 = (a1 and a2)==0 + a1 = not a1 and not a2 ; a1 = (a1 or a2)==0 } } """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! + val stmts = result.program.entrypoint.statements + stmts.size shouldBe 7 - val wwAssign = result.program.entrypoint.statements.last() as Assignment - val expr = wwAssign.value as TypecastExpression - expr.type shouldBe DataType.UWORD - - wwAssign.target.identifier?.nameInSource shouldBe listOf("ww") - expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true - } - - test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") { - val src = """ - main { - sub start() { - ubyte bb - uword ww - bb = not bb or not ww ; expression combining ubyte and uword - } - } - """ - val result = compileText(C64Target(), false, src, writeAssembly = false)!! - - // bb = ((boolean(bb)==0) or (boolean(ww)==0)) - val bbAssign = result.program.entrypoint.statements.last() as Assignment - val expr = bbAssign.value as BinaryExpression - expr.operator shouldBe "or" - expr.left shouldBe instanceOf() - expr.right shouldBe instanceOf() - expr.left.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE - expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE - expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE - - val options = CompilationOptions(OutputType.PRG, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), - floats = false, - noSysInit = true, - compTarget = C64Target(), - loadAddress = 0u, outputDir= outputDir) - result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests()) - printProgram(result.program) - - // TODO this is no longer the case: - // assignment is now split into: - // bb = not bb - // bb = (bb or (not ww as ubyte) -// val assigns = result.program.entrypoint.statements.filterIsInstance() -// val bbAssigns = assigns.filter { it.value !is NumericLiteral } -// bbAssigns.size shouldBe 2 -// -// bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb") -// bbAssigns[0].value shouldBe instanceOf() -// (bbAssigns[0].value as PrefixExpression).operator shouldBe "not" -// ((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb") -// bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE -// -// bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb") -// val bbAssigns1expr = bbAssigns[1].value as BinaryExpression -// bbAssigns1expr.operator shouldBe "or" -// (bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb") -// bbAssigns1expr.right shouldBe instanceOf() -// val castedValue = (bbAssigns1expr.right as TypecastExpression).expression as PrefixExpression -// castedValue.operator shouldBe "not" -// (castedValue.expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww") -// bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE -// -// val asm = generateAssembly(result.program, options) -// asm shouldNotBe null -// asm!!.name.shouldNotBeBlank() + val value1 = (stmts[4] as Assignment).value as BinaryExpression + val value2 = (stmts[5] as Assignment).value as BinaryExpression + val value3 = (stmts[6] as Assignment).value as BinaryExpression + value1.operator shouldBe "==" + value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + value2.operator shouldBe "==" + value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + value3.operator shouldBe "==" + value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + val left1 = value1.left as IdentifierReference + val left2 = value2.left as BinaryExpression + val left3 = value3.left as BinaryExpression + left1.nameInSource shouldBe listOf("a1") + left2.operator shouldBe "and" + (left2.left as IdentifierReference).nameInSource shouldBe listOf("a1") + (left2.right as IdentifierReference).nameInSource shouldBe listOf("a2") + left3.operator shouldBe "or" + (left3.left as IdentifierReference).nameInSource shouldBe listOf("a1") + (left3.right as IdentifierReference).nameInSource shouldBe listOf("a2") } test("intermediate assignment steps generated for typecasted expression") { diff --git a/compiler/test/TestTypecasts.kt b/compiler/test/TestTypecasts.kt index 15082c133..b841bf9a3 100644 --- a/compiler/test/TestTypecasts.kt +++ b/compiler/test/TestTypecasts.kt @@ -180,7 +180,7 @@ class TestTypecasts: FunSpec({ }""" val result = compileText(C64Target(), false, text, writeAssembly = true)!! val statements = result.program.entrypoint.statements - statements.size shouldBe 13 + statements.size shouldBe 14 } test("no infinite typecast loop in assignment asmgen") { diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index c64e8d8d7..a7ef7a44d 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -109,6 +109,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid DataType.UWORD -> NumericLiteral(DataType.UWORD, (constval.number.toInt().inv() and 65535).toDouble(), constval.position) else -> throw ExpressionError("can only take bitwise inversion of int", constval.position) } + "not" -> NumericLiteral.fromBoolean(constval.number==0.0, constval.position) else -> throw FatalAstException("invalid operator") } converted.linkParents(this.parent) @@ -214,7 +215,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex "&" -> leftDt "|" -> leftDt "^" -> leftDt - "and", "or", "xor" -> InferredTypes.knownFor(DataType.UBYTE) + "and", "or", "xor", "not" -> InferredTypes.knownFor(DataType.UBYTE) "<", ">", "<=", ">=", "==", "!=" -> dynamicBooleanType() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 74e9ab406..f382859b2 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,33 +3,32 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- code gen for if statements has become bad +- assembler incorrectly assembles hello.asm now (crash when run) + +- why can't while and until loops use NOT condition instead of Cond==0 ? Fix this! + +- code gen for if statements has become inefficient? vm/6502? if not diskio.iteration_in_progress or not num_bytes return 0 -- code gen for while loops has become bad (until loops probably as well) +- code gen for while loops has become inefficient: (until loops probably as well) (maybe solved when if statements code has been fixed) while c64.CHRIN()!='\"' { if c64.READST() goto close_end } -- chess.prg became A LOT larger, why!? (perhaps due to new while/until condition handling?) -- imageviewer.prg became A LOT larger, why!? -- petaxian.prg became A LOT larger, why!? -- some programs became a bit larger since "not" was removed (assembler) - -- 6502: fix logical and/or/xor routines to just be bitwise routines. +- petaxian.prg became quite a bit (200 bytes) larger, why!? because of the above? - get rid of logical and/or/xor in the codegen (6502+vm) because bitwise versions + correct use of boolean() operand wrapping are equivalent? - can do this for instance by replacing and/or/xor with their bitwise versions &, |, ^, ~ + can do this for instance by replacing and/or/xor with their bitwise versions &, |, ^ +- ...or: 6502: fix logical and/or/xor routines to just be bitwise routines. + +- check all examples if they still work, maybe we find bug for...: - compiling logical.p8 to virtual with optimization generates a lot larger code as without optimizations. this is not the case for the 6502 codegen. -- add optimizations: not a or not b -> not(a and b) , not a and not b -> not(a or b) - actually this now means: (a==0) or (b==0) -> (a or b)==0, (a==0) and (b==0) -> (a or b)==0 - add unit tests for that. - bin expr splitter: split logical expressions on ands/ors/xors ? - add some more optimizations in vmPeepholeOptimizer diff --git a/examples/test.p8 b/examples/test.p8 index ffa725849..e94619f2d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,22 +6,56 @@ main { sub start() { - ; a "pixelshader": - sys.gfx_enable(0) ; enable lo res screen - ubyte shifter + ubyte a1 = 0 + ubyte a2 = 42 + ubyte a3 = 1 - repeat { - uword xx - uword yy = 0 - repeat 240 { - xx = 0 - repeat 320 { - sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte) - xx++ - } - yy++ - } - shifter+=4 - } - } + if (a1==0)==0 + a3 = (a1==0)==0 + + if (a1!=0)==0 + a3 = (a1!=0)==0 + + if (a1==0)!=0 + a3 = (a1==0)!=0 + + if (a1!=0)!=0 + a3 = (a1!=0)!=0 + + if (a1==0) or (a2==0) + a3 = (a1==0) or (a2==0) + + if (a1==0) and (a2==0) + a3 = (a1==0) and (a2==0) + + if not a1 or not a2 or not(not(a3)) + a3=not a1 or not a2 or not(not(a3)) + + if (a1==0) or (a2==0) + a3 = (a1==0) or (a2==0) + + if (a1==0) and (a2==0) + a3 = (a1==0) and (a2==0) + + txt.print_ub(a3) + + +; ; a "pixelshader": +; sys.gfx_enable(0) ; enable lo res screen +; ubyte shifter +; +; repeat { +; uword xx +; uword yy = 0 +; repeat 240 { +; xx = 0 +; repeat 320 { +; sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte) +; xx++ +; } +; yy++ +; } +; shifter+=4 +; } + } }