From 036d9dbe595a356703fdbbd99ca80916e4029177 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 4 Apr 2022 23:43:55 +0200 Subject: [PATCH] got rid of unnecessary cast of boolean expressions by making their type dynamically adjust to byte or word --- .../prog8/codegen/virtual/BuiltinFuncGen.kt | 17 +++++++++++++ .../src/prog8/optimizer/BinExprSplitter.kt | 8 +++---- .../compiler/astprocessing/TypecastsAdder.kt | 4 ++++ compiler/test/TestOptimization.kt | 8 +++---- .../prog8/ast/expressions/AstExpressions.kt | 18 ++++++++++++-- docs/source/todo.rst | 1 - examples/test.p8 | 24 +++++-------------- virtualmachine/src/prog8/vm/VirtualMachine.kt | 2 +- 8 files changed, 50 insertions(+), 32 deletions(-) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 789141379..e622ef3ce 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -3,6 +3,7 @@ package prog8.codegen.virtual import prog8.code.ast.PtBuiltinFunctionCall import prog8.code.ast.PtNumber import prog8.code.ast.PtString +import prog8.code.core.WordDatatypes import prog8.vm.Opcode import prog8.vm.Syscall import prog8.vm.VmDataType @@ -82,6 +83,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) } "peek" -> { + // should just be a memory read val addressReg = codeGen.vmRegisters.nextFree() code += exprGen.translateExpression(call.args.single(), addressReg) code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg) @@ -91,6 +93,21 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: code += exprGen.translateExpression(call.args.single(), addressReg) code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg) } + "poke" -> { + // should just be a memory write + val addressReg = codeGen.vmRegisters.nextFree() + val valueReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args[0], addressReg) + code += exprGen.translateExpression(call.args[1], valueReg) + code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = addressReg, reg2=valueReg) + } + "pokew" -> { + val addressReg = codeGen.vmRegisters.nextFree() + val valueReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args[0], addressReg) + code += exprGen.translateExpression(call.args[1], valueReg) + code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = addressReg, reg2=valueReg) + } "mkword" -> { val msbReg = codeGen.vmRegisters.nextFree() val lsbReg = codeGen.vmRegisters.nextFree() diff --git a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt index 255f368cd..7f1d453ee 100644 --- a/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt +++ b/codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt @@ -3,10 +3,7 @@ package prog8.optimizer import prog8.ast.IStatementContainer import prog8.ast.Node import prog8.ast.Program -import prog8.ast.expressions.AugmentAssignmentOperators -import prog8.ast.expressions.BinaryExpression -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.TypecastExpression +import prog8.ast.expressions.* import prog8.ast.getTempVar import prog8.ast.statements.AssignTarget import prog8.ast.statements.Assignment @@ -97,7 +94,8 @@ X = BinExpr X = LeftExpr // we can see if we can unwrap the binary expression by working on a new temporary variable // (that has the type of the expression), and then finally doing the typecast. // Once it's outside the typecast, the regular splitting can commence. - val (tempVarName, _) = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED)) + val tempvarDt = origExpr.inferType(program).getOr(DataType.UNDEFINED) + val (tempVarName, _) = program.getTempVar(tempvarDt) val assignTempVar = Assignment( AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position), typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index b7dc2d5fc..0e7cfb34c 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -114,6 +114,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val if(valuetype in IterableDatatypes && targettype==DataType.UWORD) // special case, don't typecast STR/arrays to UWORD, we support those assignments "directly" return noModifications + if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators) { + // special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later + return noModifications + } val modifications = mutableListOf() addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment) return modifications diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index 38e366625..7bc7e22b8 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -253,7 +253,7 @@ class TestOptimization: FunSpec({ (initY2.value as NumericLiteral).number shouldBe 11.0 } - test("typecasted assignment from ubyte logical expressoin to uword var") { + test("not-typecasted assignment from ubyte logical expression to uword var") { val src = """ main { sub start() { @@ -265,13 +265,11 @@ class TestOptimization: FunSpec({ """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! - // ww = ((( not bb as uword) or not ww) as uword) val wwAssign = result.program.entrypoint.statements.last() as Assignment - val expr = wwAssign.value as TypecastExpression + val expr = wwAssign.value as BinaryExpression wwAssign.target.identifier?.nameInSource shouldBe listOf("ww") - expr.type shouldBe DataType.UWORD - expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true + expr.inferType(result.program) istype DataType.UWORD shouldBe true } test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") { diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index f09191b6e..debab503b 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -195,8 +195,21 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex override fun referencesIdentifier(nameInSource: List) = left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource) override fun inferType(program: Program): InferredTypes.InferredType { + val leftDt = left.inferType(program) val rightDt = right.inferType(program) + + fun dynamicBooleanType(): InferredTypes.InferredType { + // as a special case, an expression yielding a boolean result, adapts the result + // type to what is required (byte or word), to avoid useless type casting + return if(parent is TypecastExpression) + InferredTypes.InferredType.known((parent as TypecastExpression).type) + else if(parent is Assignment) + (parent as Assignment).target.inferType(program) + else + InferredTypes.InferredType.known(DataType.UBYTE) + } + return when (operator) { "+", "-", "*", "%", "/" -> { if (!leftDt.isKnown || !rightDt.isKnown) @@ -221,13 +234,14 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex "and", "or", "xor", "<", ">", "<=", ">=", - "==", "!=" -> InferredTypes.knownFor(DataType.UBYTE) + "==", "!=" -> dynamicBooleanType() "<<", ">>" -> leftDt - "in" -> InferredTypes.knownFor(DataType.UBYTE) + "in" -> dynamicBooleanType() else -> throw FatalAstException("resulting datatype check for invalid operator $operator") } } + companion object { fun commonDatatype(leftDt: DataType, rightDt: DataType, left: Expression?, right: Expression?): Pair { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3742d556f..69bd9c84e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- vm: implement all operators in the virtualmachine - vm: codegen: more optimal code for loops ending on 0 (BNZ?) - pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls. - writeAssembly(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. diff --git a/examples/test.p8 b/examples/test.p8 index 2db548b44..62858f3ad 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,27 +6,15 @@ main { - ubyte global = 42 - sub start() { - uword begin = c64.RDTIM16() + ubyte value1 = 99 + ubyte value2 = 222 - ubyte shift - repeat 60 { - ubyte yy - for yy in 0 to 59 { - ubyte xx - for xx in 0 to 79 { - ubyte color = yy+xx+shift - txt.setcc2(xx,yy,81,color) ; 356 - } - } - shift++ - } + uword @shared result = $ffff + result = value1 != value2 - uword duration = c64.RDTIM16()-begin - txt.print_uw(duration) - txt.print(" \n") + txt.print_uwhex(result, true) + txt.nl() ; a "pixelshader": ; syscall1(8, 0) ; enable lo res creen diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index 7cc15648f..8bd3548c0 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -713,7 +713,7 @@ class VirtualMachine(val memory: Memory, program: List) { when(i.type!!) { VmDataType.BYTE -> { val value = registers.getUW(i.reg1!!) - val newValue = 0xea31 // value.toUByte()*256u + (value.toInt() ushr 8).toUInt() + val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt() registers.setUW(i.reg1, newValue.toUShort()) } VmDataType.WORD -> TODO("swap.w requires 32-bits registers")