mirror of
https://github.com/irmen/prog8.git
synced 2024-10-16 18:23:59 +00:00
tweak "not" removal/rewriting
This commit is contained in:
parent
4ca0805de1
commit
97cb0cbd08
@ -3,7 +3,7 @@ package prog8.code.core
|
|||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
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("&", "|", "^")
|
val BitwiseOperators = setOf("&", "|", "^")
|
||||||
|
|
||||||
fun invertedComparisonOperator(operator: String) =
|
fun invertedComparisonOperator(operator: String) =
|
||||||
|
@ -2,7 +2,6 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ExpressionError
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.UndefinedSymbolError
|
import prog8.ast.base.UndefinedSymbolError
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -14,7 +13,6 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.AssociativeOperators
|
import prog8.code.core.AssociativeOperators
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.IntegerDatatypes
|
|
||||||
|
|
||||||
|
|
||||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
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<IAstModification> {
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// Try to turn a unary prefix expression into a single constant value.
|
val constValue = expr.constValue(program) ?: return noModifications
|
||||||
// Compile-time constant sub expressions will be evaluated on the spot.
|
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -330,6 +330,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
|||||||
errors.report()
|
errors.report()
|
||||||
program.reorderStatements(errors, compilerOptions)
|
program.reorderStatements(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
|
program.changeNotExpression(errors)
|
||||||
|
errors.report()
|
||||||
program.addTypecasts(errors, compilerOptions)
|
program.addTypecasts(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
program.variousCleanups(errors, compilerOptions)
|
program.variousCleanups(errors, compilerOptions)
|
||||||
|
@ -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) {
|
internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) {
|
||||||
val walker = object : AstWalker() {
|
val walker = object : AstWalker() {
|
||||||
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
|
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
|
||||||
|
@ -3,7 +3,6 @@ package prog8.compiler.astprocessing
|
|||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
@ -114,17 +113,6 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
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<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
val nextAssignment = decl.nextSibling() as? Assignment
|
val nextAssignment = decl.nextSibling() as? Assignment
|
||||||
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
|
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
|
||||||
@ -164,6 +152,8 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
expr
|
expr
|
||||||
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
|
else if(expr is BinaryExpression && expr.operator in LogicalOperators+ComparisonOperators)
|
||||||
expr
|
expr
|
||||||
|
else if(expr is PrefixExpression && expr.operator in LogicalOperators)
|
||||||
|
expr
|
||||||
else
|
else
|
||||||
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
||||||
}
|
}
|
||||||
|
@ -231,8 +231,12 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
var rightAssignment: Assignment? = null
|
var rightAssignment: Assignment? = null
|
||||||
var rightOperandReplacement: Expression? = null
|
var rightOperandReplacement: Expression? = null
|
||||||
|
|
||||||
val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall && expr.left !is ContainmentCheck
|
val separateLeftExpr = !expr.left.isSimple
|
||||||
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall && expr.right !is ContainmentCheck
|
&& 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 leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.*
|
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.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
@ -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<IAstModification> {
|
||||||
|
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<IAstModification> {
|
||||||
|
// 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<IAstModification> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -306,12 +306,14 @@ internal class StatementReorderer(val program: Program,
|
|||||||
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
|
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
|
||||||
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
} else {
|
}
|
||||||
|
else if(leftBinExpr.left.constValue(program)!=null && binExpr.right.constValue(program)!=null) {
|
||||||
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
||||||
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
|
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
|
||||||
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
}
|
}
|
||||||
|
else noModifications
|
||||||
}
|
}
|
||||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||||
if(rightBinExpr?.operator == binExpr.operator) {
|
if(rightBinExpr?.operator == binExpr.operator) {
|
||||||
|
@ -48,7 +48,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
if(parent is Assignment) {
|
if(parent is Assignment) {
|
||||||
val targetDt = (parent).target.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
val targetDt = (parent).target.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||||
if(sourceDt istype targetDt) {
|
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))
|
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +71,13 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
// +X --> X
|
// +X --> X
|
||||||
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
|
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
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import io.kotest.assertions.fail
|
|
||||||
import io.kotest.assertions.withClue
|
import io.kotest.assertions.withClue
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
import io.kotest.matchers.string.shouldContain
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.string.shouldNotBeBlank
|
|
||||||
import io.kotest.matchers.string.shouldStartWith
|
import io.kotest.matchers.string.shouldStartWith
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||||
@ -14,9 +12,9 @@ import prog8.ast.ParentSentinel
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
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.code.target.C64Target
|
||||||
import prog8.compiler.astprocessing.processAstBeforeAsmGeneration
|
|
||||||
import prog8.compiler.printProgram
|
import prog8.compiler.printProgram
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
|
|
||||||
@ -253,83 +251,41 @@ class TestOptimization: FunSpec({
|
|||||||
(initY2.value as NumericLiteral).number shouldBe 11.0
|
(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 = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte bb
|
ubyte a1
|
||||||
uword ww
|
ubyte a2
|
||||||
ww = not bb or not ww ; expression combining ubyte and uword
|
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 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 value1 = (stmts[4] as Assignment).value as BinaryExpression
|
||||||
val expr = wwAssign.value as TypecastExpression
|
val value2 = (stmts[5] as Assignment).value as BinaryExpression
|
||||||
expr.type shouldBe DataType.UWORD
|
val value3 = (stmts[6] as Assignment).value as BinaryExpression
|
||||||
|
value1.operator shouldBe "=="
|
||||||
wwAssign.target.identifier?.nameInSource shouldBe listOf("ww")
|
value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true
|
value2.operator shouldBe "=="
|
||||||
}
|
value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
|
value3.operator shouldBe "=="
|
||||||
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
val src = """
|
val left1 = value1.left as IdentifierReference
|
||||||
main {
|
val left2 = value2.left as BinaryExpression
|
||||||
sub start() {
|
val left3 = value3.left as BinaryExpression
|
||||||
ubyte bb
|
left1.nameInSource shouldBe listOf("a1")
|
||||||
uword ww
|
left2.operator shouldBe "and"
|
||||||
bb = not bb or not ww ; expression combining ubyte and uword
|
(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")
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
(left3.right as IdentifierReference).nameInSource shouldBe listOf("a2")
|
||||||
|
|
||||||
// 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<BinaryExpression>()
|
|
||||||
expr.right shouldBe instanceOf<BinaryExpression>()
|
|
||||||
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<Assignment>()
|
|
||||||
// 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<PrefixExpression>()
|
|
||||||
// (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<TypecastExpression>()
|
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("intermediate assignment steps generated for typecasted expression") {
|
test("intermediate assignment steps generated for typecasted expression") {
|
||||||
|
@ -180,7 +180,7 @@ class TestTypecasts: FunSpec({
|
|||||||
}"""
|
}"""
|
||||||
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 13
|
statements.size shouldBe 14
|
||||||
}
|
}
|
||||||
|
|
||||||
test("no infinite typecast loop in assignment asmgen") {
|
test("no infinite typecast loop in assignment asmgen") {
|
||||||
|
@ -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)
|
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)
|
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")
|
else -> throw FatalAstException("invalid operator")
|
||||||
}
|
}
|
||||||
converted.linkParents(this.parent)
|
converted.linkParents(this.parent)
|
||||||
@ -214,7 +215,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
"&" -> leftDt
|
"&" -> leftDt
|
||||||
"|" -> leftDt
|
"|" -> leftDt
|
||||||
"^" -> leftDt
|
"^" -> leftDt
|
||||||
"and", "or", "xor" -> InferredTypes.knownFor(DataType.UBYTE)
|
"and", "or", "xor", "not" -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
"<", ">",
|
"<", ">",
|
||||||
"<=", ">=",
|
"<=", ">=",
|
||||||
"==", "!=" -> dynamicBooleanType()
|
"==", "!=" -> dynamicBooleanType()
|
||||||
|
@ -3,33 +3,32 @@ TODO
|
|||||||
|
|
||||||
For next release
|
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
|
if not diskio.iteration_in_progress or not num_bytes
|
||||||
return 0
|
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)
|
(maybe solved when if statements code has been fixed)
|
||||||
while c64.CHRIN()!='\"' {
|
while c64.CHRIN()!='\"' {
|
||||||
if c64.READST()
|
if c64.READST()
|
||||||
goto close_end
|
goto close_end
|
||||||
}
|
}
|
||||||
|
|
||||||
- chess.prg became A LOT larger, why!? (perhaps due to new while/until condition handling?)
|
- petaxian.prg became quite a bit (200 bytes) larger, why!? because of the above?
|
||||||
- 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.
|
|
||||||
|
|
||||||
- get rid of logical and/or/xor in the codegen (6502+vm)
|
- get rid of logical and/or/xor in the codegen (6502+vm)
|
||||||
because bitwise versions + correct use of boolean() operand wrapping are equivalent?
|
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.
|
- compiling logical.p8 to virtual with optimization generates a lot larger code as without optimizations.
|
||||||
this is not the case for the 6502 codegen.
|
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 ?
|
- bin expr splitter: split logical expressions on ands/ors/xors ?
|
||||||
|
|
||||||
- add some more optimizations in vmPeepholeOptimizer
|
- add some more optimizations in vmPeepholeOptimizer
|
||||||
|
@ -6,22 +6,56 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
; a "pixelshader":
|
ubyte a1 = 0
|
||||||
sys.gfx_enable(0) ; enable lo res screen
|
ubyte a2 = 42
|
||||||
ubyte shifter
|
ubyte a3 = 1
|
||||||
|
|
||||||
repeat {
|
if (a1==0)==0
|
||||||
uword xx
|
a3 = (a1==0)==0
|
||||||
uword yy = 0
|
|
||||||
repeat 240 {
|
if (a1!=0)==0
|
||||||
xx = 0
|
a3 = (a1!=0)==0
|
||||||
repeat 320 {
|
|
||||||
sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
|
if (a1==0)!=0
|
||||||
xx++
|
a3 = (a1==0)!=0
|
||||||
}
|
|
||||||
yy++
|
if (a1!=0)!=0
|
||||||
}
|
a3 = (a1!=0)!=0
|
||||||
shifter+=4
|
|
||||||
}
|
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
|
||||||
|
; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user