tweak "not" removal/rewriting

This commit is contained in:
Irmen de Jong 2022-06-29 22:19:44 +02:00
parent 4ca0805de1
commit 97cb0cbd08
15 changed files with 243 additions and 172 deletions

View File

@ -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) =

View File

@ -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
} }
/* /*

View File

@ -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)

View File

@ -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> {

View File

@ -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)
} }

View File

@ -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)

View File

@ -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

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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
} }

View File

@ -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") {

View File

@ -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") {

View File

@ -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()

View File

@ -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

View File

@ -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
; }
}
} }