IR: optimize code for ==0 and !=0 augmented assigns

This commit is contained in:
Irmen de Jong 2024-01-24 22:36:04 +01:00
parent f2010bf7a5
commit 8cf0b6cf51
6 changed files with 105 additions and 30 deletions

View File

@ -4,10 +4,7 @@ import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PrefixOperators
import prog8.code.core.SignedDatatypes
import prog8.code.core.*
import prog8.intermediate.*
@ -30,6 +27,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val memory = augAssign.target.memory
val array = augAssign.target.array
// TODO don't fragment the implementation over multiple subroutines
val chunks = if(ident!=null) {
assignVarAugmented(ident.name, augAssign)
} else if(memory != null) {
@ -38,7 +36,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else
fallbackAssign(augAssign)
} else if(array!=null) {
assignArrayAugmented(array, augAssign)
// TODO assignArrayAugmented(array, augAssign)
fallbackAssign(augAssign)
} else {
fallbackAssign(augAssign)
}
@ -118,12 +117,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
// "<<=" -> expressionEval.operatorShiftLeftInplace(array, eltSize, value)
// ">>=" -> expressionEval.operatorShiftRightInplace(array, eltSize, signed, value)
// "%=" -> expressionEval.operatorModuloInplace(array, eltSize, value)
"==" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, true)
"!=" -> expressionEval.operatorEqualsNotEqualsInplace(array, eltSize, value, false)
// "<" -> expressionEval.operatorLessInplace(array, eltSize, signed, value) // TODO reuse code from ==
// ">" -> expressionEval.operatorGreaterInplace(array, eltSize, signed, value) // TODO reuse code from ==
// "<=" -> expressionEval.operatorLessEqualInplace(array, eltSize, signed, value) // TODO reuse code from ==
// ">=" -> expressionEval.operatorGreaterEqualInplace(array, eltSize, signed, value) // TODO reuse code from ==
"==" -> expressionEval.operatorEqualsInplace(array, eltSize, value)
"!=" -> expressionEval.operatorNotEqualsInplace(array, eltSize, value)
"<" -> expressionEval.operatorLessInplace(array, eltSize, signed, value)
">" -> expressionEval.operatorGreaterInplace(array, eltSize, signed, value)
"<=" -> expressionEval.operatorLessEqualInplace(array, eltSize, signed, value)
">=" -> expressionEval.operatorGreaterEqualInplace(array, eltSize, signed, value)
in PrefixOperators -> inplacePrefix(assignment.operator, array, eltSize)
// else -> Err(NotImplementedError("invalid augmented assign operator ${assignment.operator}"))
@ -136,12 +135,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
val value: PtExpression
if(origAssign.operator in PrefixOperators) {
value = PtPrefix(origAssign.operator, origAssign.target.type, origAssign.value.position)
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
value.add(origAssign.value)
} else {
require(origAssign.operator.endsWith('='))
val operator = if(origAssign.operator=="==") "==" else origAssign.operator.dropLast(1)
value = PtBinaryExpression(operator, origAssign.target.type, origAssign.value.position)
val operator = when(origAssign.operator) {
in ComparisonOperators -> origAssign.operator
else -> {
require(origAssign.operator.endsWith('='))
origAssign.operator.dropLast(1)
}
}
value = PtBinaryExpression(operator, origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)

View File

@ -1394,7 +1394,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
private fun createInplaceComparison(
private fun createInplaceComparison(
knownAddress: Int?,
symbol: String?,
vmDt: IRDataType,
@ -1405,7 +1405,29 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val valueReg = codeGen.registers.nextFree()
val cmpResultReg = codeGen.registers.nextFree()
if(operand is PtNumber) {
// TODO optimize if operand is 0
if(operand.number==0.0 && compareAndSetOpcode in arrayOf(Opcode.SEQ, Opcode.SNE)) {
// ==0 or !=0 optimized case
val compareAndSetOpcodeZero = if(compareAndSetOpcode==Opcode.SEQ) Opcode.SZ else Opcode.SNZ
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1 = cmpResultReg, reg2 = valueReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcodeZero, vmDt, reg1=cmpResultReg, reg2 = valueReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
return result
}
// compare against number that is not 0
val numberReg = codeGen.registers.nextFree()
if (knownAddress != null) {
// in-place modify a memory location
@ -1457,7 +1479,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val cmpReg = codeGen.registers.nextFree()
val zeroReg = codeGen.registers.nextFree()
if(operand is PtNumber) {
// TODO optimize if operand is 0 ?
val numberReg = codeGen.registers.nextFreeFloat()
val cmpResultReg = codeGen.registers.nextFree()
if (knownAddress != null) {
@ -1512,7 +1533,54 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return result
}
fun operatorEqualsNotEqualsInplace(array: PtArrayIndexer, eltSize: Int, value: PtExpression, equals: Boolean): Result<IRCodeChunks, NotImplementedError> {
fun operatorEqualsInplace(array: PtArrayIndexer, eltSize: Int, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
return createCompareArrayInplace(array, eltSize, value, Opcode.SZ)
}
fun operatorNotEqualsInplace(array: PtArrayIndexer, eltSize: Int, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
return createCompareArrayInplace(array, eltSize, value, Opcode.SNZ)
}
fun operatorLessInplace(array: PtArrayIndexer, eltSize: Int, signed: Boolean, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
TODO("<")
//return createOperatorEqualsNotEqualsInplaceComparison(array, eltSize, value, Opcode.SZ)
}
fun operatorLessEqualInplace(array: PtArrayIndexer, eltSize: Int, signed: Boolean, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
TODO("<=")
//return createOperatorEqualsNotEqualsInplaceComparison(array, eltSize, value, Opcode.SZ)
}
fun operatorGreaterInplace(array: PtArrayIndexer, eltSize: Int, signed: Boolean, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
TODO(">")
//return createOperatorEqualsNotEqualsInplaceComparison(array, eltSize, value, Opcode.SZ)
}
fun operatorGreaterEqualInplace(array: PtArrayIndexer, eltSize: Int, signed: Boolean, value: PtExpression): Result<IRCodeChunks, NotImplementedError> {
if(array.type==DataType.FLOAT)
return Err(NotImplementedError("optimized in-place compare on float arrays")) // TODO
else
TODO(">=")
//return createOperatorEqualsNotEqualsInplaceComparison(array, eltSize, value, Opcode.SZ)
}
private fun createCompareArrayInplace(array: PtArrayIndexer, eltSize: Int, value: PtExpression, compareAndSetOpcode: Opcode): Result<IRCodeChunks, NotImplementedError> {
require(array.type!=DataType.FLOAT)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
TODO("inplace == for split word array")
@ -1526,7 +1594,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(constValue!=null) {
val valueReg = codeGen.registers.nextFree()
if(constValue==0) {
val compareAndSetOpcode = if(equals) Opcode.SZ else Opcode.SNZ
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2=valueReg)

View File

@ -196,7 +196,7 @@ 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) {
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators && targettype in IntegerDatatypes) {
// special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later
return noModifications
}

View File

@ -66,9 +66,9 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
// if the expression is a comparison expression, or a logical expression, it produces the
// correct 'boolean' byte result so the cast can be removed.
// correct 'boolean' byte result so the cast can be removed. Only if target is Integer.
val binExpr = typecast.expression as? BinaryExpression
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators) {
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators && typecast.type in IntegerDatatypesNoBool) {
return listOf(IAstModification.ReplaceNode(typecast, binExpr, parent))
}

View File

@ -1,8 +1,7 @@
TODO
====
IR assignVarAugmented(): implement all operators.
IR expressionGen.kt: optimize various stuff if the operand is const value 0
IR assignArrayAugmented(): implement all operators. (BUT: actually, don't split this up anymore per assign target type ...)
maze: if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0

View File

@ -1,24 +1,29 @@
%import textio
%import floats
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
ubyte @shared xx
uword[3] ubarr
bool[3] barr
float[3] flarr
bool @shared bb
; ubarr[1] = ubarr[1] + 2
; ubarr[1] = ubarr[1] * 3
ubarr[1] = ubarr[1] < 2
ubarr[1] = ubarr[1] <= 2
ubarr[1] = ubarr[1] > 3
ubarr[1] = ubarr[1] >= 3
; barr[1] = barr[0] and barr[2]
; barr[1] = barr[0] or barr[2]
; barr[1] = barr[0] xor barr[2]
; barr[1] = not barr[0]
ubarr[1] = 999
ubarr[1] = ubarr[1]==999
txt.print_uw(ubarr[1])
; ubarr[1] = 999
; ubarr[1] = ubarr[1]==999
; txt.print_uw(ubarr[1])
; barr[1] = barr[1] and bb
; barr[1] = barr[1] or bb