fixed capitalization of operator sets to be consistent with other sets names

This commit is contained in:
Irmen de Jong 2021-12-15 23:43:14 +01:00
parent 1ff13723fe
commit 08f87c321f
18 changed files with 63 additions and 48 deletions

View File

@ -1101,7 +1101,7 @@ class AsmGen(private val program: Program,
} }
private fun requireComparisonExpression(condition: Expression) { private fun requireComparisonExpression(condition: Expression) {
if(condition !is BinaryExpression || condition.operator !in comparisonOperators) if(condition !is BinaryExpression || condition.operator !in ComparisonOperators)
throw AssemblyError("expected boolean comparison expression $condition") throw AssemblyError("expected boolean comparison expression $condition")
} }

View File

@ -543,7 +543,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes)) || (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical") throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in comparisonOperators) { if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
translateCompareStrings(expr.left, expr.operator, expr.right) translateCompareStrings(expr.left, expr.operator, expr.right)
} }
else { else {

View File

@ -45,7 +45,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
return inplaceModification(target, binExpr.operator, binExpr.right) return inplaceModification(target, binExpr.operator, binExpr.right)
} }
if (binExpr.operator in associativeOperators) { if (binExpr.operator in AssociativeOperators) {
if (binExpr.right isSameAs astTarget) { if (binExpr.right isSameAs astTarget) {
// A = 5 <operator> A // A = 5 <operator> A
return inplaceModification(target, binExpr.operator, binExpr.left) return inplaceModification(target, binExpr.operator, binExpr.left)
@ -111,7 +111,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
if(leftBinExpr.right isSameAs astTarget) { if(leftBinExpr.right isSameAs astTarget) {
// X = (Left <oper> X) <oper> Something // X = (Left <oper> X) <oper> Something
if(leftBinExpr.operator in associativeOperators) { if(leftBinExpr.operator in AssociativeOperators) {
inplaceModification(target, leftBinExpr.operator, leftBinExpr.left) inplaceModification(target, leftBinExpr.operator, leftBinExpr.left)
inplaceModification(target, binExpr.operator, binExpr.right) inplaceModification(target, binExpr.operator, binExpr.right)
return return
@ -123,7 +123,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if(leftBinExpr==null && rightBinExpr!=null) { if(leftBinExpr==null && rightBinExpr!=null) {
if(rightBinExpr.left isSameAs astTarget) { if(rightBinExpr.left isSameAs astTarget) {
// X = Something <oper> (X <oper> Right) // X = Something <oper> (X <oper> Right)
if(binExpr.operator in associativeOperators) { if(binExpr.operator in AssociativeOperators) {
inplaceModification(target, rightBinExpr.operator, rightBinExpr.right) inplaceModification(target, rightBinExpr.operator, rightBinExpr.right)
inplaceModification(target, binExpr.operator, binExpr.left) inplaceModification(target, binExpr.operator, binExpr.left)
return return
@ -133,7 +133,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
if(rightBinExpr.right isSameAs astTarget) { if(rightBinExpr.right isSameAs astTarget) {
// X = Something <oper> (Left <oper> X) // X = Something <oper> (Left <oper> X)
if(binExpr.operator in associativeOperators && rightBinExpr.operator in associativeOperators) { if(binExpr.operator in AssociativeOperators && rightBinExpr.operator in AssociativeOperators) {
inplaceModification(target, rightBinExpr.operator, rightBinExpr.left) inplaceModification(target, rightBinExpr.operator, rightBinExpr.left)
inplaceModification(target, binExpr.operator, binExpr.left) inplaceModification(target, binExpr.operator, binExpr.left)
return return

View File

@ -8,7 +8,7 @@ import prog8.ast.base.FatalAstException
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.TypecastExpression import prog8.ast.expressions.TypecastExpression
import prog8.ast.expressions.augmentAssignmentOperators import prog8.ast.expressions.AugmentAssignmentOperators
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
@ -45,7 +45,7 @@ X = BinExpr X = LeftExpr
*/ */
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) { if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.right) if(assignment.target isSameAs binExpr.right)
return noModifications return noModifications
if(assignment.target isSameAs binExpr.left) { if(assignment.target isSameAs binExpr.left) {

View File

@ -425,7 +425,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
// both operators are the same. // both operators are the same.
// If associative, we can simply shuffle the const operands around to optimize. // If associative, we can simply shuffle the const operands around to optimize.
if(expr.operator in associativeOperators) { if(expr.operator in AssociativeOperators) {
return if(leftIsConst) { return if(leftIsConst) {
if(subleftIsConst) if(subleftIsConst)
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left) ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)

View File

@ -98,11 +98,11 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
throw FatalAstException("can't determine datatype of both expression operands $expr") throw FatalAstException("can't determine datatype of both expression operands $expr")
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue // ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
if (leftVal != null && expr.operator in associativeOperators && rightVal == null) if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null)
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression // NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) { if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target)) if(parent !is Assignment || !(expr.left isSameAs parent.target))
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
} }
@ -717,7 +717,7 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants { private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
if (expr.operator in associativeOperators && leftVal != null) { if (expr.operator in AssociativeOperators && leftVal != null) {
// swap left and right so that right is always the constant // swap left and right so that right is always the constant
val tmp = expr.left val tmp = expr.left
expr.left = expr.right expr.left = expr.right

View File

@ -311,7 +311,7 @@ class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator val op1 = binExpr.operator
val op2 = rExpr.operator val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) { if(rExpr.left is NumericLiteralValue && op2 in AssociativeOperators) {
// associative operator, make sure the constant numeric value is second (right) // associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr)) return listOf(IAstModification.SwapOperands(rExpr))
} }
@ -347,7 +347,7 @@ class StatementOptimizer(private val program: Program,
} }
} }
if(binExpr.operator in associativeOperators && binExpr.right isSameAs assignment.target) { if(binExpr.operator in AssociativeOperators && binExpr.right isSameAs assignment.target) {
// associative operator, swap the operands so that the assignment target is first (left) // associative operator, swap the operands so that the assignment target is first (left)
// unless the other operand is the same in which case we don't swap (endless loop!) // unless the other operand is the same in which case we don't swap (endless loop!)
if (!(binExpr.left isSameAs binExpr.right)) if (!(binExpr.left isSameAs binExpr.right))

View File

@ -58,12 +58,12 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
if(binExpr!=null && binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions) if(binExpr!=null && binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
return noModifications return noModifications
if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr != null && binExpr.operator !in ComparisonOperators) {
if (binExpr.left !is BinaryExpression) { if (binExpr.left !is BinaryExpression) {
if (binExpr.right.referencesIdentifier(assignment.target.identifier!!.nameInSource)) { if (binExpr.right.referencesIdentifier(assignment.target.identifier!!.nameInSource)) {
// the right part of the expression contains the target variable itself. // the right part of the expression contains the target variable itself.
// we can't 'split' it trivially because the variable will be changed halfway through. // we can't 'split' it trivially because the variable will be changed halfway through.
if(binExpr.operator in associativeOperators) { if(binExpr.operator in AssociativeOperators) {
// A = <something-without-A> <associativeoperator> <otherthing-with-A> // A = <something-without-A> <associativeoperator> <otherthing-with-A>
// use the other part of the expression to split. // use the other part of the expression to split.
val sourceDt = binExpr.right.inferType(program).getOrElse { throw AssemblyError("unknown dt") } val sourceDt = binExpr.right.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
@ -191,7 +191,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
} }
val binExpr = ifStatement.condition as? BinaryExpression val binExpr = ifStatement.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) { if(binExpr==null || binExpr.operator !in ComparisonOperators) {
// if x -> if x!=0, if x+5 -> if x+5 != 0 // if x -> if x!=0, if x+5 -> if x+5 != 0
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position) val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement)) return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
@ -288,7 +288,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
} }
val binExpr = untilLoop.condition as? BinaryExpression val binExpr = untilLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) { if(binExpr==null || binExpr.operator !in ComparisonOperators) {
// until x -> until x!=0, until x+5 -> until x+5 != 0 // until x -> until x!=0, until x+5 -> until x+5 != 0
val booleanExpr = BinaryExpression(untilLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position) val booleanExpr = BinaryExpression(untilLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position)
return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop)) return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop))
@ -325,7 +325,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
} }
val binExpr = whileLoop.condition as? BinaryExpression val binExpr = whileLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) { if(binExpr==null || binExpr.operator !in ComparisonOperators) {
// while x -> while x!=0, while x+5 -> while x+5 != 0 // while x -> while x!=0, while x+5 -> while x+5 != 0
val booleanExpr = BinaryExpression(whileLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position) val booleanExpr = BinaryExpression(whileLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position)
return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop)) return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop))

View File

@ -863,7 +863,7 @@ internal class AstChecker(private val program: Program,
if(leftDt!=rightDt) if(leftDt!=rightDt)
errors.err("left and right operands aren't the same type", expr.left.position) errors.err("left and right operands aren't the same type", expr.left.position)
if(expr.operator !in comparisonOperators) { if(expr.operator !in ComparisonOperators) {
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) { if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
// str+str and str*number have already been const evaluated before we get here. // str+str and str*number have already been const evaluated before we get here.
errors.err("no computational expressions with strings or arrays are possible", expr.position) errors.err("no computational expressions with strings or arrays are possible", expr.position)

View File

@ -170,7 +170,7 @@ internal class StatementReorderer(val program: Program,
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue // ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
// (this should be done by the ExpressionSimplifier when optimizing is enabled, // (this should be done by the ExpressionSimplifier when optimizing is enabled,
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.) // but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
if (expr.left.constValue(program) != null && expr.operator in associativeOperators && expr.right.constValue(program) == null) if (expr.left.constValue(program) != null && expr.operator in AssociativeOperators && expr.right.constValue(program) == null)
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
// when using a simple bit shift and assigning it to a variable of a different type, // when using a simple bit shift and assigning it to a variable of a different type,
@ -220,7 +220,7 @@ internal class StatementReorderer(val program: Program,
else -> return noModifications else -> return noModifications
} }
} }
else if(expr.operator in logicalOperators) { else if(expr.operator in LogicalOperators) {
// make sure that logical expressions like "var and other-logical-expression // make sure that logical expressions like "var and other-logical-expression
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and // is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
// generating the wrong results later // generating the wrong results later
@ -229,9 +229,9 @@ internal class StatementReorderer(val program: Program,
BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0.0, expr.position), expr.position) BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0.0, expr.position), expr.position)
fun isLogicalExpr(expr: Expression?): Boolean { fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (logicalOperators + comparisonOperators)) if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
return true return true
if(expr is PrefixExpression && expr.operator in logicalOperators) if(expr is PrefixExpression && expr.operator in LogicalOperators)
return true return true
return false return false
} }
@ -295,7 +295,7 @@ internal class StatementReorderer(val program: Program,
return noModifications return noModifications
} }
if(binExpr.operator in associativeOperators) { if(binExpr.operator in AssociativeOperators) {
if (binExpr.right isSameAs assignment.target) { if (binExpr.right isSameAs assignment.target) {
// A = v <associative-operator> A ==> A = A <associative-operator> v // A = v <associative-operator> A ==> A = A <associative-operator> v
return listOf(IAstModification.SwapOperands(binExpr)) return listOf(IAstModification.SwapOperands(binExpr))
@ -392,6 +392,12 @@ internal class StatementReorderer(val program: Program,
return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent)) return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent))
} }
// if(function.parameters.size==1) {
// // 1 param
// val dt = function.parameters[0].type
// if(dt in IntegerDatatypes)
// }
val assignParams = val assignParams =
function.parameters.zip(call.args).map { function.parameters.zip(call.args).map {
var argumentValue = it.second var argumentValue = it.second

View File

@ -49,7 +49,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) { if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
// convert a negative operand for bitwise operator to the 2's complement positive number instead // convert a negative operand for bitwise operator to the 2's complement positive number instead
if(expr.operator in bitwiseOperators && leftDt.isInteger && rightDt.isInteger) { if(expr.operator in BitwiseOperators && leftDt.isInteger && rightDt.isInteger) {
val leftCv = expr.left.constValue(program) val leftCv = expr.left.constValue(program)
if(leftCv!=null && leftCv.number<0) { if(leftCv!=null && leftCv.number<0) {
val value = if(rightDt.isBytes) 256+leftCv.number else 65536+leftCv.number val value = if(rightDt.isBytes) 256+leftCv.number else 65536+leftCv.number

View File

@ -310,7 +310,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null && binExpr.left isSameAs assignment.target if(binExpr!=null && binExpr.left isSameAs assignment.target
&& binExpr.operator !in arrayOf("and", "or", "xor") && binExpr.operator !in arrayOf("and", "or", "xor")
&& binExpr.operator !in comparisonOperators) { && binExpr.operator !in ComparisonOperators) {
// we only support the inplace assignments of the form A = A <operator> <value> // we only support the inplace assignments of the form A = A <operator> <value>
assignment.target.accept(this) assignment.target.accept(this)
output(" ${binExpr.operator}= ") output(" ${binExpr.operator}= ")

View File

@ -9,12 +9,11 @@ import prog8.ast.walk.IAstVisitor
import java.util.* import java.util.*
import kotlin.math.round import kotlin.math.round
// TODO capitalize for consistency 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")
val logicalOperators = setOf("and", "or", "xor", "not") val BitwiseOperators = setOf("&", "|", "^")
val bitwiseOperators = setOf("&", "|", "^")
sealed class Expression: Node { sealed class Expression: Node {

View File

@ -348,7 +348,7 @@ open class Assignment(var target: AssignTarget, var value: Expression, final ove
} }
} }
if(binExpr.operator in associativeOperators) { if(binExpr.operator in AssociativeOperators) {
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target) if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
return true // A = v <associative-operator> A return true // A = v <associative-operator> A

View File

@ -67,7 +67,7 @@ interface IAstModification {
class SwapOperands(private val expr: BinaryExpression): IAstModification { class SwapOperands(private val expr: BinaryExpression): IAstModification {
override fun perform() { override fun perform() {
require(expr.operator in associativeOperators) require(expr.operator in AssociativeOperators)
val tmp = expr.left val tmp = expr.left
expr.left = expr.right expr.left = expr.right
expr.right = tmp expr.right = tmp

View File

@ -9,11 +9,6 @@ non-asm subroutines with just a single byte or word parameter:
add code to set the parameter variable in the start of the subroutine itself, add code to set the parameter variable in the start of the subroutine itself,
rather than requiring the caller to set it there. This is not faster but saves a lot of bytes of code. rather than requiring the caller to set it there. This is not faster but saves a lot of bytes of code.
rewrite multiple choice if into when:
if X==1 or X==2 or X==3 { truepart } else { falsepart }
-> when X { 1,2,3->truepart else->falsepart }
same with assignment if the lhs is simple var or memaddr
... ...
@ -54,6 +49,11 @@ More code optimization ideas
- while-expression should now also get the simplifyConditionalExpression() treatment - while-expression should now also get the simplifyConditionalExpression() treatment
- byte typed expressions should be evaluated in the accumulator where possible, without (temp)var - byte typed expressions should be evaluated in the accumulator where possible, without (temp)var
for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value
- rewrite multiple choice if into when:
if X==1 or X==2 or X==3 { truepart } else { falsepart }
-> when X { 1,2,3->truepart else->falsepart }
same with assignment if the lhs is simple var or memaddr
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables' - rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'
- this removes the need for the BinExprSplitter? (which is problematic and very limited now) - this removes the need for the BinExprSplitter? (which is problematic and very limited now)
- introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4 - introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4

View File

@ -1,12 +1,22 @@
%import floats
main { main {
sub start() { sub start() {
uword xx = 10
if xx+99 == 1.23456 {
xx++
}
if xx+99 == 1234567 { singleparamb(10)
xx++ singleparamw(2000)
} singleparamf(1.23456)
}
sub singleparamb(ubyte bb) {
bb++
}
sub singleparamw(word ww) {
ww++
}
sub singleparamf(float ff) {
ff++
} }
} }

View File

@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true org.gradle.daemon=true
kotlin.code.style=official kotlin.code.style=official
javaVersion=11 javaVersion=11
kotlinVersion=1.6.0 kotlinVersion=1.6.10