From 75a06d2a40c1d6c53813cd3bbff83bcd68d0c40f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 12 Nov 2021 02:17:37 +0100 Subject: [PATCH] preparing for more optimizations --- .../compiler/BeforeAsmGenerationAstChanger.kt | 2 +- .../compiler/astprocessing/TypecastsAdder.kt | 2 +- .../compiler/astprocessing/VariousCleanups.kt | 13 +++++ compiler/test/TestOptimization.kt | 33 +++++++++++++ .../prog8/ast/expressions/AstExpressions.kt | 4 +- compilerAst/test/TestProg8Parser.kt | 47 +++++++++++++++++++ examples/test.p8 | 2 +- 7 files changed, 98 insertions(+), 5 deletions(-) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index aba12ff40..43bfa723f 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -146,7 +146,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o } override fun after(typecast: TypecastExpression, parent: Node): Iterable { - // see if we can remove superfluous typecasts (outside of expressions) + // see if we can remove redundant typecasts (outside of expressions) // such as casting byte<->ubyte, word<->uword // Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of, // UNLESS it's a str parameter in the containing subroutine - then we remove the typecast altogether diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index d15ed635e..2ab85c1f1 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -47,7 +47,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk val rightDt = expr.right.inferType(program) if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) { // determine common datatype and add typecast as required to make left and right equal types - val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.right) + val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOr(DataType.UNDEFINED), rightDt.getOr(DataType.UNDEFINED), expr.left, expr.operator, expr.right) if(toFix!=null) { return when { toFix===expr.left -> listOf(IAstModification.ReplaceNode( diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 2ffd8a867..576ff6f06 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -74,6 +74,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter) if(sourceDt istype typecast.type) return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) + // TODO finish this; get rid of redundant final typecast to variable's type if they are the same +// 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 +// // TODO REMOVE IT +// println("TYPECAST IN ASSIGNMENT $parent") // TODO DEBUG +// } else { +// // TODO +// println("${typecast.expression} : source=$sourceDt to $targetDt") +// } +// } + return noModifications } diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 0635c3976..7c95112d9 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -190,6 +190,39 @@ class TestOptimization: FunSpec({ asm.valid shouldBe true } + test("intermediate assignment steps 2 have correct types for codegen phase (BeforeAsmGenerationAstChanger)") { + val src = """ + main { + sub start() { + ubyte r + ubyte @shared bb = (cos8(r)/2 + 100) as ubyte + } + } + """ + val result = compileText(C64Target, true, src, writeAssembly = false).assertSuccess() + + // bb = (cos8(r)/2 + 100) as ubyte + val bbAssign = result.program.entrypoint.statements.last() as Assignment + val texpr = bbAssign.value as TypecastExpression + texpr.type shouldBe DataType.UBYTE + texpr.expression shouldBe instanceOf() + texpr.expression.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.BYTE + + val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target) + val changer = BeforeAsmGenerationAstChanger(result.program, + options, + ErrorReporterForTests() + ) + + changer.visit(result.program) + while(changer.applyModifications()>0) { + changer.visit(result.program) + } + + // printAst(result.program) + // TODO finish this test + } + test("asmgen correctly deals with float typecasting in augmented assignment") { val src=""" %option enable_floats diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 8fc8d4159..ae954ea83 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -170,7 +170,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex commonDatatype( leftDt.getOr(DataType.BYTE), rightDt.getOr(DataType.BYTE), - null, null + null, "", null ).first ) } catch (x: FatalAstException) { @@ -192,7 +192,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex companion object { fun commonDatatype(leftDt: DataType, rightDt: DataType, - left: Expression?, right: Expression?): Pair { + left: Expression?, operator: String, right: Expression?): Pair { // byte + byte -> byte // byte + word -> word // word + byte -> word diff --git a/compilerAst/test/TestProg8Parser.kt b/compilerAst/test/TestProg8Parser.kt index f39cf0a5d..b9bfb7c58 100644 --- a/compilerAst/test/TestProg8Parser.kt +++ b/compilerAst/test/TestProg8Parser.kt @@ -675,4 +675,51 @@ class TestProg8Parser: FunSpec( { expr.right.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD expr.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE } + + test("inferred type for typecasted expressions with logical operators") { + val src=SourceCode.Text(""" + main { + ubyte bb + uword ww + uword qq = (not bb as uword) + uword zz = not bb or not ww + ubyte bb2 = not bb or not ww + uword zz2 = (not bb as uword) or not ww + } + """) + val module = parseModule(src) + val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder) + module.linkIntoProgram(program) + val stmts = (module.statements.single() as Block).statements + stmts.size shouldBe 6 + val qq = (stmts[2] as VarDecl).value as TypecastExpression + val zz = (stmts[3] as VarDecl).value as BinaryExpression + val bb2 = (stmts[4] as VarDecl).value as BinaryExpression + val zz2 = (stmts[5] as VarDecl).value as BinaryExpression + qq.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD + zz.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE + bb2.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE + + zz2.operator shouldBe "or" + val left = zz2.left as TypecastExpression + val right = zz2.right as PrefixExpression + left.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD + right.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UWORD + zz2.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE // 'or' causes UBYTE result + } + + test("type cast from byte to ubyte as desired target type") { + val src = SourceCode.Text(""" + main { + ubyte r + ubyte ub = (cos8(r)/2 + 100) as ubyte + }""") + val module = parseModule(src) + val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder) + module.linkIntoProgram(program) + val stmts = (module.statements.single() as Block).statements + stmts.size shouldBe 2 + val ubexpr = (stmts[1] as VarDecl).value as TypecastExpression + ubexpr.inferType(program).getOrElse { fail("dt") } shouldBe DataType.UBYTE + } }) diff --git a/examples/test.p8 b/examples/test.p8 index 72ed36f36..a692e3997 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,7 +7,7 @@ main { sub start() { ubyte bb uword ww - ww = not bb or not ww ; TODO WHY DOES THIS USE STACK EVAL + uword @shared zz = not bb or not ww ; TODO WHY DOES THIS USE STACK EVAL-because of typecastings? }