got rid of unnecessary cast of boolean expressions by making their type dynamically adjust to byte or word

This commit is contained in:
Irmen de Jong 2022-04-04 23:43:55 +02:00
parent 1d342cc6af
commit 036d9dbe59
8 changed files with 50 additions and 32 deletions

View File

@ -3,6 +3,7 @@ package prog8.codegen.virtual
import prog8.code.ast.PtBuiltinFunctionCall import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtNumber import prog8.code.ast.PtNumber
import prog8.code.ast.PtString import prog8.code.ast.PtString
import prog8.code.core.WordDatatypes
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.Syscall import prog8.vm.Syscall
import prog8.vm.VmDataType 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) code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
} }
"peek" -> { "peek" -> {
// should just be a memory read
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg) code += exprGen.translateExpression(call.args.single(), addressReg)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=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 += exprGen.translateExpression(call.args.single(), addressReg)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=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" -> { "mkword" -> {
val msbReg = codeGen.vmRegisters.nextFree() val msbReg = codeGen.vmRegisters.nextFree()
val lsbReg = codeGen.vmRegisters.nextFree() val lsbReg = codeGen.vmRegisters.nextFree()

View File

@ -3,10 +3,7 @@ package prog8.optimizer
import prog8.ast.IStatementContainer import prog8.ast.IStatementContainer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.AugmentAssignmentOperators import prog8.ast.expressions.*
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.TypecastExpression
import prog8.ast.getTempVar import prog8.ast.getTempVar
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment 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 // 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. // (that has the type of the expression), and then finally doing the typecast.
// Once it's outside the typecast, the regular splitting can commence. // 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( val assignTempVar = Assignment(
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position), AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position

View File

@ -114,6 +114,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(valuetype in IterableDatatypes && targettype==DataType.UWORD) if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly" // special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
return noModifications 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<IAstModification>() val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment) addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
return modifications return modifications

View File

@ -253,7 +253,7 @@ class TestOptimization: FunSpec({
(initY2.value as NumericLiteral).number shouldBe 11.0 (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 = """ val src = """
main { main {
sub start() { sub start() {
@ -265,13 +265,11 @@ class TestOptimization: FunSpec({
""" """
val result = compileText(C64Target(), false, src, writeAssembly = false)!! 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 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") wwAssign.target.identifier?.nameInSource shouldBe listOf("ww")
expr.type shouldBe DataType.UWORD expr.inferType(result.program) istype DataType.UWORD shouldBe true
expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true
} }
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") { test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {

View File

@ -195,8 +195,21 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
override fun referencesIdentifier(nameInSource: List<String>) = left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource) override fun referencesIdentifier(nameInSource: List<String>) = left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
val leftDt = left.inferType(program) val leftDt = left.inferType(program)
val rightDt = right.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) { return when (operator) {
"+", "-", "*", "%", "/" -> { "+", "-", "*", "%", "/" -> {
if (!leftDt.isKnown || !rightDt.isKnown) if (!leftDt.isKnown || !rightDt.isKnown)
@ -221,13 +234,14 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
"and", "or", "xor", "and", "or", "xor",
"<", ">", "<", ">",
"<=", ">=", "<=", ">=",
"==", "!=" -> InferredTypes.knownFor(DataType.UBYTE) "==", "!=" -> dynamicBooleanType()
"<<", ">>" -> leftDt "<<", ">>" -> leftDt
"in" -> InferredTypes.knownFor(DataType.UBYTE) "in" -> dynamicBooleanType()
else -> throw FatalAstException("resulting datatype check for invalid operator $operator") else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
} }
} }
companion object { companion object {
fun commonDatatype(leftDt: DataType, rightDt: DataType, fun commonDatatype(leftDt: DataType, rightDt: DataType,
left: Expression?, right: Expression?): Pair<DataType, Expression?> { left: Expression?, right: Expression?): Pair<DataType, Expression?> {

View File

@ -3,7 +3,6 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- vm: implement all operators in the virtualmachine
- vm: codegen: more optimal code for loops ending on 0 (BNZ?) - 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. - 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. - writeAssembly(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.

View File

@ -6,27 +6,15 @@
main { main {
ubyte global = 42
sub start() { sub start() {
uword begin = c64.RDTIM16() ubyte value1 = 99
ubyte value2 = 222
ubyte shift uword @shared result = $ffff
repeat 60 { result = value1 != value2
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 duration = c64.RDTIM16()-begin txt.print_uwhex(result, true)
txt.print_uw(duration) txt.nl()
txt.print(" \n")
; a "pixelshader": ; a "pixelshader":
; syscall1(8, 0) ; enable lo res creen ; syscall1(8, 0) ; enable lo res creen

View File

@ -713,7 +713,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
when(i.type!!) { when(i.type!!) {
VmDataType.BYTE -> { VmDataType.BYTE -> {
val value = registers.getUW(i.reg1!!) 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()) registers.setUW(i.reg1, newValue.toUShort())
} }
VmDataType.WORD -> TODO("swap.w requires 32-bits registers") VmDataType.WORD -> TODO("swap.w requires 32-bits registers")