From 3ea76d56a563175a4a66f3ea68ce7d1e5b6cd157 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 22:05:16 +0700 Subject: [PATCH 01/37] Implement kmath-asm module stubs --- kmath-asm/build.gradle.kts | 9 + .../kmath/expressions/AsmExpressions.kt | 385 ++++++++++++++++++ .../kmath/expressions/MethodVisitors.kt | 17 + .../scientifik/kmath/expressions/AsmTest.kt | 23 ++ kmath-core/build.gradle.kts | 2 +- .../kmath/expressions/Expression.kt | 12 +- ...xpressions.kt => FunctionalExpressions.kt} | 0 .../{bigNumbers.kt => BigNumbers.kt} | 0 settings.gradle.kts | 3 +- 9 files changed, 443 insertions(+), 8 deletions(-) create mode 100644 kmath-asm/build.gradle.kts create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt create mode 100644 kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt rename kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/{functionalExpressions.kt => FunctionalExpressions.kt} (100%) rename kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/{bigNumbers.kt => BigNumbers.kt} (100%) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts new file mode 100644 index 000000000..ab4684cdc --- /dev/null +++ b/kmath-asm/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("scientifik.jvm") +} + +dependencies { + api(project(path = ":kmath-core")) + api("org.ow2.asm:asm:8.0.1") + api("org.ow2.asm:asm-commons:8.0.1") +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt new file mode 100644 index 000000000..b9c357671 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -0,0 +1,385 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes.* +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space +import java.io.File + +abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { + abstract fun evaluate(arguments: Map): T +} + +class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra, private val className: String) { + private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) + } + + private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + + @Suppress("PrivatePropertyName") + private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + + @Suppress("PrivatePropertyName") + private val T_CLASS: String = classOfT.name.replace('.', '/') + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter = ClassWriter(0) + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + private val evaluateMethodVisitor: MethodVisitor + private val evaluateThisVar: Int = 0 + private val evaluateArgumentsVar: Int = 1 + private var evaluateL0: Label + private lateinit var evaluateL1: Label + var maxStack: Int = 0 + + init { + asmCompiledClassWriter.visit( + V1_8, + ACC_PUBLIC or ACC_FINAL or ACC_SUPER, + slashesClassName, + "L$ASM_COMPILED_CLASS;", + ASM_COMPILED_CLASS, + arrayOf() + ) + + asmCompiledClassWriter.run { + visitMethod(ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitVarInsn(ALOAD, thisVar) + visitVarInsn(ALOAD, algebraVar) + visitVarInsn(ALOAD, constantsVar) + + visitMethodInsn( + INVOKESPECIAL, + ASM_COMPILED_CLASS, + "", + "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + false + ) + + val l1 = Label() + visitLabel(l1) + visitInsn(RETURN) + val l2 = Label() + visitLabel(l2) + visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + + visitLocalVariable( + "algebra", + "L$ALGEBRA_CLASS;", + "L$ALGEBRA_CLASS;", + l0, + l2, + algebraVar + ) + + visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitMaxs(3, 3) + visitEnd() + } + + evaluateMethodVisitor = visitMethod( + ACC_PUBLIC or ACC_FINAL, + "evaluate", + "(L$MAP_CLASS;)L$T_CLASS;", + "(L$MAP_CLASS;)L$T_CLASS;", + null + ) + + evaluateMethodVisitor.run { + visitCode() + evaluateL0 = Label() + visitLabel(evaluateL0) + } + } + } + + @Suppress("UNCHECKED_CAST") + fun generate(): AsmCompiled { + evaluateMethodVisitor.run { + visitInsn(ARETURN) + evaluateL1 = Label() + visitLabel(evaluateL1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + evaluateL0, + evaluateL1, + evaluateThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + evaluateL0, + evaluateL1, + evaluateArgumentsVar + ) + + visitMaxs(maxStack + 1, 2) + visitEnd() + } + + asmCompiledClassWriter.visitMethod( + ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC, + "evaluate", + "(L$MAP_CLASS;)L$OBJECT_CLASS;", + null, + null + ).run { + val thisVar = 0 + visitCode() + val l0 = Label() + visitLabel(l0) + visitVarInsn(ALOAD, 0) + visitVarInsn(ALOAD, 1) + visitMethodInsn(INVOKEVIRTUAL, slashesClassName, "evaluate", "(L$MAP_CLASS;)L$T_CLASS;", false) + visitInsn(ARETURN) + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(2, 2) + visitEnd() + } + + asmCompiledClassWriter.visitEnd() + + return classLoader + .defineClass(className, asmCompiledClassWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants) as AsmCompiled + } + + fun visitLoadFromConstants(value: T) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + maxStack++ + + evaluateMethodVisitor.run { + visitLoadThis() + visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitLdcOrIConstInsn(idx) + visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + visitCastToT() + } + } + + private fun visitLoadThis(): Unit = evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + + fun visitNumberConstant(value: Number): Unit = evaluateMethodVisitor.visitLdcInsn(value) + + fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { + maxStack++ + visitVarInsn(ALOAD, evaluateArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + visitLoadFromConstants(defaultValue) + + visitMethodInsn( + INVOKEINTERFACE, + MAP_CLASS, + "getOrDefault", + "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", + true + ) + + visitCastToT() + return + } + + visitLdcInsn(name) + visitMethodInsn(INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitCastToT() + } + + fun visitLoadAlgebra() { + evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + + evaluateMethodVisitor.visitFieldInsn( + GETFIELD, + ASM_COMPILED_CLASS, "algebra", "L$ALGEBRA_CLASS;" + ) + + evaluateMethodVisitor.visitTypeInsn(CHECKCAST, T_ALGEBRA_CLASS) + } + + fun visitInvokeAlgebraOperation(owner: String, method: String, descriptor: String) { + maxStack++ + evaluateMethodVisitor.visitMethodInsn(INVOKEINTERFACE, owner, method, descriptor, true) + visitCastToT() + } + + fun visitCastToT() { + evaluateMethodVisitor.visitTypeInsn(CHECKCAST, T_CLASS) + } + + companion object { + const val ASM_COMPILED_CLASS = "scientifik/kmath/expressions/AsmCompiled" + const val LIST_CLASS = "java/util/List" + const val MAP_CLASS = "java/util/Map" + const val OBJECT_CLASS = "java/lang/Object" + const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + const val SPACE_CLASS = "scientifik/kmath/operations/Space" + const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + const val FIELD_CLASS = "scientifik/kmath/operations/Field" + const val STRING_CLASS = "java/lang/String" + } +} + +interface AsmExpression { + fun invoke(gen: AsmGenerationContext) +} + +internal class AsmVariableExpression(val name: String, val default: T? = null) : + AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadFromVariables(name, default) + } +} + +internal class AsmConstantExpression(val value: T) : + AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadFromConstants(value) + } +} + +internal class AsmSumExpression( + val first: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + first.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + method = "add", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmProductExpression( + val first: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + first.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_CLASS, + method = "times", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmConstProductExpression( + val expr: AsmExpression, + val const: Number +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + expr.invoke(gen) + gen.visitNumberConstant(const) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_CLASS, + method = "multiply", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmDivExpression( + val expr: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + expr.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.FIELD_CLASS, + method = "divide", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + + +open class AsmFunctionalExpressionSpace( + val space: Space, + one: T +) : Space>, + ExpressionSpace> { + override val zero: AsmExpression = + AsmConstantExpression(space.zero) + + override fun const(value: T): AsmExpression = + AsmConstantExpression(value) + + override fun variable(name: String, default: T?): AsmExpression = + AsmVariableExpression( + name, + default + ) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmSumExpression(a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = + AsmConstProductExpression(a, k) + + + operator fun AsmExpression.plus(arg: T) = this + const(arg) + operator fun AsmExpression.minus(arg: T) = this - const(arg) + + operator fun T.plus(arg: AsmExpression) = arg + this + operator fun T.minus(arg: AsmExpression) = arg - this +} + +class AsmFunctionalExpressionField(val field: Field) : ExpressionField>, + AsmFunctionalExpressionSpace(field, field.one) { + override val one: AsmExpression + get() = const(this.field.one) + + override fun const(value: Double): AsmExpression = const(field.run { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmProductExpression(a, b) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmDivExpression(a, b) + + operator fun AsmExpression.times(arg: T) = this * const(arg) + operator fun AsmExpression.div(arg: T) = this / const(arg) + + operator fun T.times(arg: AsmExpression) = arg * this + operator fun T.div(arg: AsmExpression) = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt new file mode 100644 index 000000000..fdbc1062e --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -0,0 +1,17 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { + when (value) { + -1 -> visitInsn(Opcodes.ICONST_M1) + 0 -> visitInsn(Opcodes.ICONST_0) + 1 -> visitInsn(Opcodes.ICONST_1) + 2 -> visitInsn(Opcodes.ICONST_2) + 3 -> visitInsn(Opcodes.ICONST_3) + 4 -> visitInsn(Opcodes.ICONST_4) + 5 -> visitInsn(Opcodes.ICONST_5) + else -> visitLdcInsn(value) + } +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt new file mode 100644 index 000000000..e95f8df76 --- /dev/null +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -0,0 +1,23 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class AsmTest { + @Test + fun test() { + val expr = AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")) + + val gen = AsmGenerationContext( + java.lang.Double::class.java, + RealField, + "MyAsmCompiled" + ) + + expr.invoke(gen) + val compiled = gen.generate() + val value = compiled.evaluate(mapOf("x" to 25.0)) + assertEquals(26.0, value) + } +} diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 092f3deb7..18c0cc771 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -8,4 +8,4 @@ kotlin.sourceSets { api(project(":kmath-memory")) } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index 94bc81413..7b781e4e1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -15,7 +15,7 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext> { +interface ExpressionContext { /** * Introduce a variable into expression context */ @@ -29,11 +29,11 @@ interface ExpressionContext> { fun produce(node: SyntaxTreeNode): E } -interface ExpressionSpace> : Space, ExpressionContext { +interface ExpressionSpace : Space, ExpressionContext { - open fun produceSingular(value: String): E = variable(value) + fun produceSingular(value: String): E = variable(value) - open fun produceUnary(operation: String, value: E): E { + fun produceUnary(operation: String, value: E): E { return when (operation) { UnaryNode.PLUS_OPERATION -> value UnaryNode.MINUS_OPERATION -> -value @@ -41,7 +41,7 @@ interface ExpressionSpace> : Space, ExpressionContext left + right BinaryNode.MINUS_OPERATION -> left - right @@ -75,7 +75,7 @@ interface ExpressionSpace> : Space, ExpressionContext> : Field, ExpressionSpace { +interface ExpressionField : Field, ExpressionSpace { fun const(value: Double): E = one.times(value) override fun produce(node: SyntaxTreeNode): E { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt similarity index 100% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt similarity index 100% rename from kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt rename to kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..fcaa72698 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,5 +44,6 @@ include( ":kmath-dimensions", ":kmath-for-real", ":kmath-geometry", - ":examples" + ":examples", + ":kmath-asm" ) From fdd2551c3f8efd6efa76e4d7c09e74ab18a1aa35 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 22:12:39 +0700 Subject: [PATCH 02/37] Minor refactor --- .../kmath/expressions/AsmExpressions.kt | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index b9c357671..36607d2b5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,7 +7,6 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T @@ -223,15 +222,13 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra { internal class AsmVariableExpression(val name: String, val default: T? = null) : AsmExpression { - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadFromVariables(name, default) - } + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(val value: T) : AsmExpression { - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadFromConstants(value) - } + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmSumExpression( @@ -273,7 +267,7 @@ internal class AsmSumExpression( first.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( + gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "add", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" @@ -290,8 +284,8 @@ internal class AsmProductExpression( first.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( - owner = AsmGenerationContext.SPACE_CLASS, + gen.visitAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "times", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) @@ -307,7 +301,7 @@ internal class AsmConstProductExpression( expr.invoke(gen) gen.visitNumberConstant(const) - gen.visitInvokeAlgebraOperation( + gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_CLASS, method = "multiply", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" @@ -324,8 +318,8 @@ internal class AsmDivExpression( expr.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( - owner = AsmGenerationContext.FIELD_CLASS, + gen.visitAlgebraOperation( + owner = AsmGenerationContext.FIELD_OPERATIONS_CLASS, method = "divide", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) From 557142c2bab38bffe63de44e4f6dee79d3324291 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 23:02:16 +0700 Subject: [PATCH 03/37] Add more tests, fix constant product and product operations impl. --- .../kmath/expressions/AsmExpressions.kt | 25 ++++--- .../kmath/expressions/MethodVisitors.kt | 33 ++++++--- .../scientifik/kmath/expressions/AsmTest.kt | 71 +++++++++++++++---- 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 36607d2b5..9a7ad69f2 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,6 +7,7 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space +import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T @@ -184,10 +185,13 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra( second.invoke(gen) gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "times", + owner = AsmGenerationContext.RING_OPERATIONS_CLASS, + method = "multiply", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) } @@ -298,13 +302,13 @@ internal class AsmConstProductExpression( ) : AsmExpression { override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - expr.invoke(gen) gen.visitNumberConstant(const) + expr.invoke(gen) gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_CLASS, + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) } } @@ -326,7 +330,6 @@ internal class AsmDivExpression( } } - open class AsmFunctionalExpressionSpace( val space: Space, one: T diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index fdbc1062e..5b9a8afe8 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -1,17 +1,34 @@ package scientifik.kmath.expressions import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes +import org.objectweb.asm.Opcodes.* fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { when (value) { - -1 -> visitInsn(Opcodes.ICONST_M1) - 0 -> visitInsn(Opcodes.ICONST_0) - 1 -> visitInsn(Opcodes.ICONST_1) - 2 -> visitInsn(Opcodes.ICONST_2) - 3 -> visitInsn(Opcodes.ICONST_3) - 4 -> visitInsn(Opcodes.ICONST_4) - 5 -> visitInsn(Opcodes.ICONST_5) + -1 -> visitInsn(ICONST_M1) + 0 -> visitInsn(ICONST_0) + 1 -> visitInsn(ICONST_1) + 2 -> visitInsn(ICONST_2) + 3 -> visitInsn(ICONST_3) + 4 -> visitInsn(ICONST_4) + 5 -> visitInsn(ICONST_5) else -> visitLdcInsn(value) } } + +private val signatureLetters = mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D", + java.lang.Short::class.java to "S" +) + +fun MethodVisitor.visitBoxedNumberConstant(number: Number) { + val clazz = number.javaClass + val c = clazz.name.replace('.', '/') + visitLdcInsn(number) + visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index e95f8df76..402307050 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -1,23 +1,68 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals class AsmTest { - @Test - fun test() { - val expr = AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")) - - val gen = AsmGenerationContext( - java.lang.Double::class.java, - RealField, - "MyAsmCompiled" + private fun testExpressionValue( + expectedValue: T, + expr: AsmExpression, + arguments: Map, + algebra: Algebra, + clazz: Class<*> + ) { + assertEquals( + expectedValue, AsmGenerationContext( + clazz, + algebra, + "TestAsmCompiled" + ).also(expr::invoke).generate().evaluate(arguments) ) - - expr.invoke(gen) - val compiled = gen.generate() - val value = compiled.evaluate(mapOf("x" to 25.0)) - assertEquals(26.0, value) } + + @Suppress("UNCHECKED_CAST") + private fun testDoubleExpressionValue( + expectedValue: Double, + expr: AsmExpression, + arguments: Map, + algebra: Algebra = RealField, + clazz: Class = java.lang.Double::class.java as Class + ) = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + + @Test + fun testSum() = testDoubleExpressionValue( + 25.0, + AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")), + mapOf("x" to 24.0) + ) + + @Test + fun testConst() = testDoubleExpressionValue( + 123.0, + AsmConstantExpression(123.0), + mapOf() + ) + + @Test + fun testDiv() = testDoubleExpressionValue( + 0.5, + AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), + mapOf() + ) + + @Test + fun testProduct() = testDoubleExpressionValue( + 25.0, + AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), + mapOf("x" to 5.0) + ) + + @Test + fun testCProduct() = testDoubleExpressionValue( + 25.0, + AsmConstProductExpression(AsmVariableExpression("x"), 5.0), + mapOf("x" to 5.0) + ) } From d6d9351c9c15bf8780a1bc16953d98b37d9650cb Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 23:04:04 +0700 Subject: [PATCH 04/37] Add more tests --- .../kotlin/scientifik/kmath/expressions/AsmTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 402307050..98aa3706b 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -65,4 +65,18 @@ class AsmTest { AsmConstProductExpression(AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) ) + + @Test + fun testVar() = testDoubleExpressionValue( + 10000.0, + AsmVariableExpression("x"), + mapOf("x" to 10000.0) + ) + + @Test + fun testVarWithDefault() = testDoubleExpressionValue( + 10000.0, + AsmVariableExpression("x", 10000.0), + mapOf() + ) } From 723bd84417e168f067d59d84ab20330b9efa8423 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:06:15 +0700 Subject: [PATCH 05/37] Remove unused import --- .../main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 9a7ad69f2..f4951081b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,7 +7,6 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T From 30b32c15159118f1a93d1f2d1f466711c656c6ee Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:45:15 +0700 Subject: [PATCH 06/37] Move initialization code to separate method to make AsmGenerationContext restartable --- .../kmath/expressions/AsmExpressions.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index f4951081b..876e7abe2 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -24,17 +24,26 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra = mutableListOf() - private val asmCompiledClassWriter = ClassWriter(0) + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') - private val evaluateMethodVisitor: MethodVisitor private val evaluateThisVar: Int = 0 private val evaluateArgumentsVar: Int = 1 - private var evaluateL0: Label + private var maxStack: Int = 0 + private lateinit var constants: MutableList + private lateinit var asmCompiledClassWriter: ClassWriter + private lateinit var evaluateMethodVisitor: MethodVisitor + private lateinit var evaluateL0: Label private lateinit var evaluateL1: Label - var maxStack: Int = 0 init { + start() + } + + fun start() { + constants = mutableListOf() + asmCompiledClassWriter = ClassWriter(0) + maxStack = 0 + asmCompiledClassWriter.visit( V1_8, ACC_PUBLIC or ACC_FINAL or ACC_SUPER, From 41094e63da244314b07bb7043d519600b018d9dc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:48:52 +0700 Subject: [PATCH 07/37] Optimize number constants placing to economize contant pool places --- .../kmath/expressions/MethodVisitors.kt | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index 5b9a8afe8..dc5d83ea5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -3,17 +3,28 @@ package scientifik.kmath.expressions import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { - when (value) { - -1 -> visitInsn(ICONST_M1) - 0 -> visitInsn(ICONST_0) - 1 -> visitInsn(ICONST_1) - 2 -> visitInsn(ICONST_2) - 3 -> visitInsn(ICONST_3) - 4 -> visitInsn(ICONST_4) - 5 -> visitInsn(ICONST_5) - else -> visitLdcInsn(value) - } +fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { + -1 -> visitInsn(ICONST_M1) + 0 -> visitInsn(ICONST_0) + 1 -> visitInsn(ICONST_1) + 2 -> visitInsn(ICONST_2) + 3 -> visitInsn(ICONST_3) + 4 -> visitInsn(ICONST_4) + 5 -> visitInsn(ICONST_5) + else -> visitLdcInsn(value) +} + +fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { + 0.0 -> visitInsn(DCONST_0) + 1.0 -> visitInsn(DCONST_1) + else -> visitLdcInsn(value) +} + +fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { + 0f -> visitInsn(FCONST_0) + 1f -> visitInsn(FCONST_1) + 2f -> visitInsn(FCONST_2) + else -> visitLdcInsn(value) } private val signatureLetters = mapOf( @@ -29,6 +40,13 @@ private val signatureLetters = mapOf( fun MethodVisitor.visitBoxedNumberConstant(number: Number) { val clazz = number.javaClass val c = clazz.name.replace('.', '/') - visitLdcInsn(number) + + when (number) { + is Int -> visitLdcOrIConstInsn(number) + is Double -> visitLdcOrDConstInsn(number) + is Float -> visitLdcOrFConstInsn(number) + else -> visitLdcInsn(number) + } + visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) } From cdb24ea8e2c798850e09c22f12c44cd03e16e2f0 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:51:22 +0700 Subject: [PATCH 08/37] Remove duplicate Short key from signatureLetters map --- .../main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index dc5d83ea5..7ca488846 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -33,8 +33,7 @@ private val signatureLetters = mapOf( java.lang.Integer::class.java to "I", java.lang.Long::class.java to "J", java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D", - java.lang.Short::class.java to "S" + java.lang.Double::class.java to "D" ) fun MethodVisitor.visitBoxedNumberConstant(number: Number) { From fb74e74b01b780cc7d389140ad61cb06d3d383f6 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 22:05:25 +0700 Subject: [PATCH 09/37] Remove constant number allocation hack and support uncommon Number implementations to be available in constants --- .../kmath/expressions/AsmExpressions.kt | 36 ++++++++++++++++--- .../kmath/expressions/MethodVisitors.kt | 23 ------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 876e7abe2..1b44903cd 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -8,7 +8,7 @@ import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { +abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T } @@ -29,7 +29,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra + private lateinit var constants: MutableList private lateinit var asmCompiledClassWriter: ClassWriter private lateinit var evaluateMethodVisitor: MethodVisitor private lateinit var evaluateL0: Label @@ -178,7 +178,9 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra } - fun visitLoadFromConstants(value: T) { + fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any) + + fun visitLoadAnyFromConstants(value: Any) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ @@ -195,7 +197,24 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra evaluateMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> evaluateMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> evaluateMethodVisitor.visitLdcOrFConstInsn(value) + else -> evaluateMethodVisitor.visitLdcInsn(value) + } + + evaluateMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + return + } + + visitLoadAnyFromConstants(value) } fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { @@ -243,6 +262,15 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra visitInsn(FCONST_2) else -> visitLdcInsn(value) } - -private val signatureLetters = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" -) - -fun MethodVisitor.visitBoxedNumberConstant(number: Number) { - val clazz = number.javaClass - val c = clazz.name.replace('.', '/') - - when (number) { - is Int -> visitLdcOrIConstInsn(number) - is Double -> visitLdcOrDConstInsn(number) - is Float -> visitLdcOrFConstInsn(number) - else -> visitLdcInsn(number) - } - - visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) -} From 6686144538268343e47856d39b26fc8a42d17e01 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 22:09:18 +0700 Subject: [PATCH 10/37] Add type casts for constants --- .../kotlin/scientifik/kmath/expressions/AsmExpressions.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 1b44903cd..8060674e5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -178,9 +178,9 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra } - fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any) + fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - fun visitLoadAnyFromConstants(value: Any) { + fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ @@ -189,7 +189,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra Date: Sun, 7 Jun 2020 15:57:23 +0700 Subject: [PATCH 11/37] Add more test for ASM const product --- .../scientifik/kmath/expressions/AsmTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 98aa3706b..9ab6f26c9 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -66,6 +66,30 @@ class AsmTest { mapOf("x" to 5.0) ) + @Test + fun testCProductWithOtherTypeNumber() = testDoubleExpressionValue( + 25.0, + AsmConstProductExpression(AsmVariableExpression("x"), 5f), + mapOf("x" to 5.0) + ) + + object CustomZero : Number() { + override fun toByte(): Byte = 0 + override fun toChar(): Char = 0.toChar() + override fun toDouble(): Double = 0.0 + override fun toFloat(): Float = 0f + override fun toInt(): Int = 0 + override fun toLong(): Long = 0L + override fun toShort(): Short = 0 + } + + @Test + fun testCProductWithCustomTypeNumber() = testDoubleExpressionValue( + 0.0, + AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), + mapOf("x" to 5.0) + ) + @Test fun testVar() = testDoubleExpressionValue( 10000.0, From 6ac0297530010799e2ede800f93a7a50e8c891b0 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Sun, 7 Jun 2020 19:04:39 +0700 Subject: [PATCH 12/37] Move asm dependency to implementation configuration; rename many ASM API classes, make AsmCompiledExpression implement functional Expression, fix typos, encapsulate AsmGenerationContext --- kmath-asm/build.gradle.kts | 4 +- .../kmath/expressions/AsmExpressions.kt | 199 +++++++++--------- .../scientifik/kmath/expressions/AsmTest.kt | 25 +-- .../kmath/expressions/Expression.kt | 2 +- .../expressions/FunctionalExpressions.kt | 46 ++-- 5 files changed, 129 insertions(+), 147 deletions(-) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts index ab4684cdc..4104349f8 100644 --- a/kmath-asm/build.gradle.kts +++ b/kmath-asm/build.gradle.kts @@ -4,6 +4,6 @@ plugins { dependencies { api(project(path = ":kmath-core")) - api("org.ow2.asm:asm:8.0.1") - api("org.ow2.asm:asm-commons:8.0.1") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 8060674e5..f196efffe 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -8,11 +8,30 @@ import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { - abstract fun evaluate(arguments: Map): T +abstract class AsmCompiledExpression internal constructor( + @JvmField val algebra: Algebra, + @JvmField val constants: MutableList +) : Expression { + abstract override fun invoke(arguments: Map): T } -class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra, private val className: String) { +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + +class AsmGenerationContext( + classOfT: Class<*>, + private val algebra: Algebra, + private val className: String +) { private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } @@ -26,30 +45,23 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra - private lateinit var asmCompiledClassWriter: ClassWriter - private lateinit var evaluateMethodVisitor: MethodVisitor - private lateinit var evaluateL0: Label - private lateinit var evaluateL1: Label + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) + private val invokeMethodVisitor: MethodVisitor + private val invokeL0: Label + private lateinit var invokeL1: Label + private var generatedInstance: AsmCompiledExpression? = null init { - start() - } - - fun start() { - constants = mutableListOf() - asmCompiledClassWriter = ClassWriter(0) - maxStack = 0 - asmCompiledClassWriter.visit( V1_8, ACC_PUBLIC or ACC_FINAL or ACC_SUPER, slashesClassName, - "L$ASM_COMPILED_CLASS;", - ASM_COMPILED_CLASS, + "L$ASM_COMPILED_EXPRESSION_CLASS;", + ASM_COMPILED_EXPRESSION_CLASS, arrayOf() ) @@ -66,7 +78,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", false @@ -93,45 +105,48 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra;)L$T_CLASS;", null ) - evaluateMethodVisitor.run { + invokeMethodVisitor.run { visitCode() - evaluateL0 = Label() - visitLabel(evaluateL0) + invokeL0 = Label() + visitLabel(invokeL0) } } } + @PublishedApi @Suppress("UNCHECKED_CAST") - fun generate(): AsmCompiled { - evaluateMethodVisitor.run { + internal fun generate(): AsmCompiledExpression { + generatedInstance?.let { return it } + + invokeMethodVisitor.run { visitInsn(ARETURN) - evaluateL1 = Label() - visitLabel(evaluateL1) + invokeL1 = Label() + visitLabel(invokeL1) visitLocalVariable( "this", "L$slashesClassName;", T_CLASS, - evaluateL0, - evaluateL1, - evaluateThisVar + invokeL0, + invokeL1, + invokeThisVar ) visitLocalVariable( "arguments", "L$MAP_CLASS;", "L$MAP_CLASS;", - evaluateL0, - evaluateL1, - evaluateArgumentsVar + invokeL0, + invokeL1, + invokeArgumentsVar ) visitMaxs(maxStack + 1, 2) @@ -140,7 +155,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra + .newInstance(algebra, constants) as AsmCompiledExpression + + generatedInstance = new + return new } - fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - fun visitLoadAnyFromConstants(value: Any, type: String) { + private fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ - evaluateMethodVisitor.run { + invokeMethodVisitor.run { visitLoadThis() visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") visitLdcOrIConstInsn(idx) visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) - evaluateMethodVisitor.visitTypeInsn(CHECKCAST, type) + invokeMethodVisitor.visitTypeInsn(CHECKCAST, type) } } - private fun visitLoadThis(): Unit = evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - fun visitNumberConstant(value: Number) { + internal fun visitNumberConstant(value: Number) { maxStack++ val clazz = value.javaClass val c = clazz.name.replace('.', '/') @@ -204,22 +222,22 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra evaluateMethodVisitor.visitLdcOrIConstInsn(value) - is Double -> evaluateMethodVisitor.visitLdcOrDConstInsn(value) - is Float -> evaluateMethodVisitor.visitLdcOrFConstInsn(value) - else -> evaluateMethodVisitor.visitLdcInsn(value) + is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) + else -> invokeMethodVisitor.visitLdcInsn(value) } - evaluateMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + invokeMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) return } visitLoadAnyFromConstants(value, c) } - fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { maxStack += 2 - visitVarInsn(ALOAD, evaluateArgumentsVar) + visitVarInsn(ALOAD, invokeArgumentsVar) if (defaultValue != null) { visitLdcInsn(name) @@ -242,26 +260,26 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra( } } -open class AsmFunctionalExpressionSpace( - val space: Space, - one: T -) : Space>, +open class AsmExpressionSpace(space: Space) : Space>, ExpressionSpace> { - override val zero: AsmExpression = - AsmConstantExpression(space.zero) - - override fun const(value: T): AsmExpression = - AsmConstantExpression(value) - - override fun variable(name: String, default: T?): AsmExpression = - AsmVariableExpression( - name, - default - ) - - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmSumExpression(a, b) - - override fun multiply(a: AsmExpression, k: Number): AsmExpression = - AsmConstProductExpression(a, k) - - + override val zero: AsmExpression = AsmConstantExpression(space.zero) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) operator fun AsmExpression.plus(arg: T) = this + const(arg) operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this operator fun T.minus(arg: AsmExpression) = arg - this } -class AsmFunctionalExpressionField(val field: Field) : ExpressionField>, - AsmFunctionalExpressionSpace(field, field.one) { +class AsmExpressionField(private val field: Field) : ExpressionField>, + AsmExpressionSpace(field) { override val one: AsmExpression get() = const(this.field.one) - override fun const(value: Double): AsmExpression = const(field.run { one * value }) + override fun number(value: Number): AsmExpression = const(field.run { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) @@ -412,7 +412,6 @@ class AsmFunctionalExpressionField(val field: Field) : ExpressionField.times(arg: T) = this * const(arg) operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.times(arg: AsmExpression) = arg * this operator fun T.div(arg: AsmExpression) = arg / this } diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 9ab6f26c9..875080616 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -12,15 +12,12 @@ class AsmTest { arguments: Map, algebra: Algebra, clazz: Class<*> - ) { - assertEquals( - expectedValue, AsmGenerationContext( - clazz, - algebra, - "TestAsmCompiled" - ).also(expr::invoke).generate().evaluate(arguments) - ) - } + ): Unit = assertEquals( + expectedValue, AsmGenerationContext(clazz, algebra, "TestAsmCompiled") + .also(expr::invoke) + .generate() + .invoke(arguments) + ) @Suppress("UNCHECKED_CAST") private fun testDoubleExpressionValue( @@ -29,7 +26,7 @@ class AsmTest { arguments: Map, algebra: Algebra = RealField, clazz: Class = java.lang.Double::class.java as Class - ) = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + ): Unit = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) @Test fun testSum() = testDoubleExpressionValue( @@ -39,28 +36,28 @@ class AsmTest { ) @Test - fun testConst() = testDoubleExpressionValue( + fun testConst(): Unit = testDoubleExpressionValue( 123.0, AsmConstantExpression(123.0), mapOf() ) @Test - fun testDiv() = testDoubleExpressionValue( + fun testDiv(): Unit = testDoubleExpressionValue( 0.5, AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), mapOf() ) @Test - fun testProduct() = testDoubleExpressionValue( + fun testProduct(): Unit = testDoubleExpressionValue( 25.0, AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), mapOf("x" to 5.0) ) @Test - fun testCProduct() = testDoubleExpressionValue( + fun testCProduct(): Unit = testDoubleExpressionValue( 25.0, AsmConstProductExpression(AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index 7b781e4e1..dae069879 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -76,7 +76,7 @@ interface ExpressionSpace : Space, ExpressionContext { } interface ExpressionField : Field, ExpressionSpace { - fun const(value: Double): E = one.times(value) + fun number(value: Number): E = one * value override fun produce(node: SyntaxTreeNode): E { if (node is BinaryNode) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index ad0acbc4a..dbc2eac1e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -27,55 +27,41 @@ internal class ProductExpression(val context: Ring, val first: Expression< context.multiply(first.invoke(arguments), second.invoke(arguments)) } -internal class ConstProductExpession(val context: Space, val expr: Expression, val const: Number) : +internal class ConstProductExpression(val context: Space, val expr: Expression, val const: Number) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } -internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : +internal class DivExpression(val context: Field, val expr: Expression, val second: Expression) : Expression { override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) } -open class FunctionalExpressionSpace( - val space: Space, - one: T -) : Space>, ExpressionSpace> { - +open class FunctionalExpressionSpace(val space: Space) : Space>, ExpressionSpace> { override val zero: Expression = ConstantExpression(space.zero) override fun const(value: T): Expression = ConstantExpression(value) - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) - - - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this + override fun multiply(a: Expression, k: Number): Expression = ConstProductExpression(space, a, k) + operator fun Expression.plus(arg: T): Expression = this + const(arg) + operator fun Expression.minus(arg: T): Expression = this - const(arg) + operator fun T.plus(arg: Expression): Expression = arg + this + operator fun T.minus(arg: Expression): Expression = arg - this } open class FunctionalExpressionField( val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field, field.one) { +) : ExpressionField>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - override fun const(value: Double): Expression = const(field.run { one*value}) - + override fun number(value: Number): Expression = const(field.run { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) - - override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) - - operator fun Expression.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - - operator fun T.times(arg: Expression) = arg * this - operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file + override fun divide(a: Expression, b: Expression): Expression = DivExpression(field, a, b) + operator fun Expression.times(arg: T): Expression = this * const(arg) + operator fun Expression.div(arg: T): Expression = this / const(arg) + operator fun T.times(arg: Expression): Expression = arg * this + operator fun T.div(arg: Expression): Expression = arg / this +} From 013030951ee8cd0c8319c5504d07704a91a90901 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Sun, 7 Jun 2020 19:23:39 +0700 Subject: [PATCH 13/37] Make AsmCompiledExpression fields private, add builder functions not to expose AsmGenerationContext to public API, refactor code --- .../kmath/expressions/AsmExpressionSpaces.kt | 31 ++ .../kmath/expressions/AsmExpressions.kt | 329 +----------------- .../kmath/expressions/AsmGenerationContext.kt | 283 +++++++++++++++ .../scientifik/kmath/expressions/Builders.kt | 38 ++ .../scientifik/kmath/expressions/AsmTest.kt | 8 +- 5 files changed, 358 insertions(+), 331 deletions(-) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt new file mode 100644 index 000000000..5c93fd729 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space + +open class AsmExpressionSpace(space: Space) : Space>, + ExpressionSpace> { + override val zero: AsmExpression = AsmConstantExpression(space.zero) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this +} + +class AsmExpressionField(private val field: Field) : ExpressionField>, + AsmExpressionSpace(field) { + override val one: AsmExpression + get() = const(this.field.one) + + override fun number(value: Number): AsmExpression = const(field.run { one * value }) + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(a, b) + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index f196efffe..8708a33e1 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -1,307 +1,14 @@ package scientifik.kmath.expressions -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Label -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space abstract class AsmCompiledExpression internal constructor( - @JvmField val algebra: Algebra, - @JvmField val constants: MutableList + @JvmField private val algebra: Algebra, + @JvmField private val constants: MutableList ) : Expression { abstract override fun invoke(arguments: Map): T } -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - -class AsmGenerationContext( - classOfT: Class<*>, - private val algebra: Algebra, - private val className: String -) { - private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { - internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) - } - - private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) - - @Suppress("PrivatePropertyName") - private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') - - @Suppress("PrivatePropertyName") - private val T_CLASS: String = classOfT.name.replace('.', '/') - - private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') - private val invokeThisVar: Int = 0 - private val invokeArgumentsVar: Int = 1 - private var maxStack: Int = 0 - private val constants: MutableList = mutableListOf() - private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) - private val invokeMethodVisitor: MethodVisitor - private val invokeL0: Label - private lateinit var invokeL1: Label - private var generatedInstance: AsmCompiledExpression? = null - - init { - asmCompiledClassWriter.visit( - V1_8, - ACC_PUBLIC or ACC_FINAL or ACC_SUPER, - slashesClassName, - "L$ASM_COMPILED_EXPRESSION_CLASS;", - ASM_COMPILED_EXPRESSION_CLASS, - arrayOf() - ) - - asmCompiledClassWriter.run { - visitMethod(ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { - val thisVar = 0 - val algebraVar = 1 - val constantsVar = 2 - val l0 = Label() - visitLabel(l0) - visitVarInsn(ALOAD, thisVar) - visitVarInsn(ALOAD, algebraVar) - visitVarInsn(ALOAD, constantsVar) - - visitMethodInsn( - INVOKESPECIAL, - ASM_COMPILED_EXPRESSION_CLASS, - "", - "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", - false - ) - - val l1 = Label() - visitLabel(l1) - visitInsn(RETURN) - val l2 = Label() - visitLabel(l2) - visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) - - visitLocalVariable( - "algebra", - "L$ALGEBRA_CLASS;", - "L$ALGEBRA_CLASS;", - l0, - l2, - algebraVar - ) - - visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) - visitMaxs(3, 3) - visitEnd() - } - - invokeMethodVisitor = visitMethod( - ACC_PUBLIC or ACC_FINAL, - "invoke", - "(L$MAP_CLASS;)L$T_CLASS;", - "(L$MAP_CLASS;)L$T_CLASS;", - null - ) - - invokeMethodVisitor.run { - visitCode() - invokeL0 = Label() - visitLabel(invokeL0) - } - } - } - - @PublishedApi - @Suppress("UNCHECKED_CAST") - internal fun generate(): AsmCompiledExpression { - generatedInstance?.let { return it } - - invokeMethodVisitor.run { - visitInsn(ARETURN) - invokeL1 = Label() - visitLabel(invokeL1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - invokeL0, - invokeL1, - invokeThisVar - ) - - visitLocalVariable( - "arguments", - "L$MAP_CLASS;", - "L$MAP_CLASS;", - invokeL0, - invokeL1, - invokeArgumentsVar - ) - - visitMaxs(maxStack + 1, 2) - visitEnd() - } - - asmCompiledClassWriter.visitMethod( - ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC, - "invoke", - "(L$MAP_CLASS;)L$OBJECT_CLASS;", - null, - null - ).run { - val thisVar = 0 - visitCode() - val l0 = Label() - visitLabel(l0) - visitVarInsn(ALOAD, 0) - visitVarInsn(ALOAD, 1) - visitMethodInsn(INVOKEVIRTUAL, slashesClassName, "invoke", "(L$MAP_CLASS;)L$T_CLASS;", false) - visitInsn(ARETURN) - val l1 = Label() - visitLabel(l1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - l0, - l1, - thisVar - ) - - visitMaxs(2, 2) - visitEnd() - } - - asmCompiledClassWriter.visitEnd() - - val new = classLoader - .defineClass(className, asmCompiledClassWriter.toByteArray()) - .constructors - .first() - .newInstance(algebra, constants) as AsmCompiledExpression - - generatedInstance = new - return new - } - - internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - - private fun visitLoadAnyFromConstants(value: Any, type: String) { - val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex - maxStack++ - - invokeMethodVisitor.run { - visitLoadThis() - visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") - visitLdcOrIConstInsn(idx) - visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) - invokeMethodVisitor.visitTypeInsn(CHECKCAST, type) - } - } - - private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - - internal fun visitNumberConstant(value: Number) { - maxStack++ - val clazz = value.javaClass - val c = clazz.name.replace('.', '/') - - val sigLetter = SIGNATURE_LETTERS[clazz] - - if (sigLetter != null) { - when (value) { - is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) - is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) - is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) - else -> invokeMethodVisitor.visitLdcInsn(value) - } - - invokeMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) - return - } - - visitLoadAnyFromConstants(value, c) - } - - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { - maxStack += 2 - visitVarInsn(ALOAD, invokeArgumentsVar) - - if (defaultValue != null) { - visitLdcInsn(name) - visitLoadFromConstants(defaultValue) - - visitMethodInsn( - INVOKEINTERFACE, - MAP_CLASS, - "getOrDefault", - "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", - true - ) - - visitCastToT() - return - } - - visitLdcInsn(name) - visitMethodInsn(INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) - visitCastToT() - } - - internal fun visitLoadAlgebra() { - invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - - invokeMethodVisitor.visitFieldInsn( - GETFIELD, - ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" - ) - - invokeMethodVisitor.visitTypeInsn(CHECKCAST, T_ALGEBRA_CLASS) - } - - internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { - maxStack++ - invokeMethodVisitor.visitMethodInsn(INVOKEINTERFACE, owner, method, descriptor, true) - visitCastToT() - } - - private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(CHECKCAST, T_CLASS) - - internal companion object { - private val SIGNATURE_LETTERS = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) - - internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" - internal const val LIST_CLASS = "java/util/List" - internal const val MAP_CLASS = "java/util/Map" - internal const val OBJECT_CLASS = "java/lang/Object" - internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" - internal const val STRING_CLASS = "java/lang/String" - internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" - internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" - internal const val NUMBER_CLASS = "java/lang/Number" - } -} - interface AsmExpression { fun invoke(gen: AsmGenerationContext) } @@ -383,35 +90,3 @@ internal class AsmDivExpression( ) } } - -open class AsmExpressionSpace(space: Space) : Space>, - ExpressionSpace> { - override val zero: AsmExpression = AsmConstantExpression(space.zero) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) - operator fun AsmExpression.plus(arg: T) = this + const(arg) - operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this - operator fun T.minus(arg: AsmExpression) = arg - this -} - -class AsmExpressionField(private val field: Field) : ExpressionField>, - AsmExpressionSpace(field) { - override val one: AsmExpression - get() = const(this.field.one) - - override fun number(value: Number): AsmExpression = const(field.run { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmProductExpression(a, b) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmDivExpression(a, b) - - operator fun AsmExpression.times(arg: T) = this * const(arg) - operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.times(arg: AsmExpression) = arg * this - operator fun T.div(arg: AsmExpression) = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt new file mode 100644 index 000000000..cedd5c0fd --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt @@ -0,0 +1,283 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import scientifik.kmath.operations.Algebra + +class AsmGenerationContext( + classOfT: Class<*>, + private val algebra: Algebra, + private val className: String +) { + private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) + } + + private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + + @Suppress("PrivatePropertyName") + private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + + @Suppress("PrivatePropertyName") + private val T_CLASS: String = classOfT.name.replace('.', '/') + + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + private val invokeThisVar: Int = 0 + private val invokeArgumentsVar: Int = 1 + private var maxStack: Int = 0 + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter: ClassWriter = + ClassWriter(0) + private val invokeMethodVisitor: MethodVisitor + private val invokeL0: Label + private lateinit var invokeL1: Label + private var generatedInstance: AsmCompiledExpression? = null + + init { + asmCompiledClassWriter.visit( + Opcodes.V1_8, + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, + slashesClassName, + "L$ASM_COMPILED_EXPRESSION_CLASS;", + ASM_COMPILED_EXPRESSION_CLASS, + arrayOf() + ) + + asmCompiledClassWriter.run { + visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitVarInsn(Opcodes.ALOAD, thisVar) + visitVarInsn(Opcodes.ALOAD, algebraVar) + visitVarInsn(Opcodes.ALOAD, constantsVar) + + visitMethodInsn( + Opcodes.INVOKESPECIAL, + ASM_COMPILED_EXPRESSION_CLASS, + "", + "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + false + ) + + val l1 = Label() + visitLabel(l1) + visitInsn(Opcodes.RETURN) + val l2 = Label() + visitLabel(l2) + visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + + visitLocalVariable( + "algebra", + "L$ALGEBRA_CLASS;", + "L$ALGEBRA_CLASS;", + l0, + l2, + algebraVar + ) + + visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitMaxs(3, 3) + visitEnd() + } + + invokeMethodVisitor = visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + "invoke", + "(L$MAP_CLASS;)L$T_CLASS;", + "(L$MAP_CLASS;)L$T_CLASS;", + null + ) + + invokeMethodVisitor.run { + visitCode() + invokeL0 = Label() + visitLabel(invokeL0) + } + } + } + + @PublishedApi + @Suppress("UNCHECKED_CAST") + internal fun generate(): AsmCompiledExpression { + generatedInstance?.let { return it } + + invokeMethodVisitor.run { + visitInsn(Opcodes.ARETURN) + invokeL1 = Label() + visitLabel(invokeL1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + invokeL0, + invokeL1, + invokeThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + invokeL0, + invokeL1, + invokeArgumentsVar + ) + + visitMaxs(maxStack + 1, 2) + visitEnd() + } + + asmCompiledClassWriter.visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + "invoke", + "(L$MAP_CLASS;)L$OBJECT_CLASS;", + null, + null + ).run { + val thisVar = 0 + visitCode() + val l0 = Label() + visitLabel(l0) + visitVarInsn(Opcodes.ALOAD, 0) + visitVarInsn(Opcodes.ALOAD, 1) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, slashesClassName, "invoke", "(L$MAP_CLASS;)L$T_CLASS;", false) + visitInsn(Opcodes.ARETURN) + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(2, 2) + visitEnd() + } + + asmCompiledClassWriter.visitEnd() + + val new = classLoader + .defineClass(className, asmCompiledClassWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants) as AsmCompiledExpression + + generatedInstance = new + return new + } + + internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + + private fun visitLoadAnyFromConstants(value: Any, type: String) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + maxStack++ + + invokeMethodVisitor.run { + visitLoadThis() + visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitLdcOrIConstInsn(idx) + visitMethodInsn(Opcodes.INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type) + } + } + + private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) + + internal fun visitNumberConstant(value: Number) { + maxStack++ + val clazz = value.javaClass + val c = clazz.name.replace('.', '/') + + val sigLetter = SIGNATURE_LETTERS[clazz] + + if (sigLetter != null) { + when (value) { + is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) + else -> invokeMethodVisitor.visitLdcInsn(value) + } + + invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + return + } + + visitLoadAnyFromConstants(value, c) + } + + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { + maxStack += 2 + visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + visitLoadFromConstants(defaultValue) + + visitMethodInsn( + Opcodes.INVOKEINTERFACE, + MAP_CLASS, + "getOrDefault", + "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", + true + ) + + visitCastToT() + return + } + + visitLdcInsn(name) + visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitCastToT() + } + + internal fun visitLoadAlgebra() { + invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) + + invokeMethodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" + ) + + invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) + } + + internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { + maxStack++ + invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, method, descriptor, true) + visitCastToT() + } + + private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) + + internal companion object { + private val SIGNATURE_LETTERS = mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) + + internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" + internal const val LIST_CLASS = "java/util/List" + internal const val MAP_CLASS = "java/util/Map" + internal const val OBJECT_CLASS = "java/lang/Object" + internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + internal const val STRING_CLASS = "java/lang/String" + internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" + internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" + internal const val NUMBER_CLASS = "java/lang/Number" + } +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt new file mode 100644 index 000000000..bdbab2b42 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt @@ -0,0 +1,38 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space + +@PublishedApi +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + + +inline fun asmSpace( + algebra: Space, + block: AsmExpressionSpace.() -> AsmExpression +): Expression { + val expression = AsmExpressionSpace(algebra).block() + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) + expression.invoke(ctx) + return ctx.generate() +} + +inline fun asmField( + algebra: Field, + block: AsmExpressionField.() -> AsmExpression +): Expression { + val expression = AsmExpressionField(algebra).block() + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) + expression.invoke(ctx) + return ctx.generate() +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 875080616..4370029f6 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -64,7 +64,7 @@ class AsmTest { ) @Test - fun testCProductWithOtherTypeNumber() = testDoubleExpressionValue( + fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( 25.0, AsmConstProductExpression(AsmVariableExpression("x"), 5f), mapOf("x" to 5.0) @@ -81,21 +81,21 @@ class AsmTest { } @Test - fun testCProductWithCustomTypeNumber() = testDoubleExpressionValue( + fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( 0.0, AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), mapOf("x" to 5.0) ) @Test - fun testVar() = testDoubleExpressionValue( + fun testVar(): Unit = testDoubleExpressionValue( 10000.0, AsmVariableExpression("x"), mapOf("x" to 10000.0) ) @Test - fun testVarWithDefault() = testDoubleExpressionValue( + fun testVarWithDefault(): Unit = testDoubleExpressionValue( 10000.0, AsmVariableExpression("x", 10000.0), mapOf() From b7d1fe256023acf0b03d48fb77a55c5119b453e8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:38:37 +0700 Subject: [PATCH 14/37] Implement recursive constants evaluation, improve builders --- .../kmath/expressions/AsmExpressionSpaces.kt | 13 +++-- .../kmath/expressions/AsmExpressions.kt | 49 +++++++++++++++---- .../scientifik/kmath/expressions/Builders.kt | 20 ++++---- .../kmath/expressions/Optimization.kt | 6 +++ 4 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index 5c93fd729..da788372b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -3,13 +3,13 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -open class AsmExpressionSpace(space: Space) : Space>, +open class AsmExpressionSpace(private val space: Space) : Space>, ExpressionSpace> { override val zero: AsmExpression = AsmConstantExpression(space.zero) override fun const(value: T): AsmExpression = AsmConstantExpression(value) override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(space, a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this @@ -22,8 +22,11 @@ class AsmExpressionField(private val field: Field) : ExpressionField = const(field.run { one * value }) - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(a, b) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmProductExpression(field, a, b) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(field, a, b) operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.times(arg: AsmExpression): AsmExpression = arg * this diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 8708a33e1..cc861d363 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.* abstract class AsmCompiledExpression internal constructor( @JvmField private val algebra: Algebra, @@ -10,6 +10,7 @@ abstract class AsmCompiledExpression internal constructor( } interface AsmExpression { + fun tryEvaluate(): T? = null fun invoke(gen: AsmGenerationContext) } @@ -20,13 +21,22 @@ internal class AsmVariableExpression(val name: String, val default: T? = null internal class AsmConstantExpression(val value: T) : AsmExpression { + override fun tryEvaluate(): T = value override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmSumExpression( - val first: AsmExpression, - val second: AsmExpression + private val algebra: SpaceOperations, + first: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (first.tryEvaluate() ?: return@algebra null) + (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() first.invoke(gen) @@ -41,9 +51,17 @@ internal class AsmSumExpression( } internal class AsmProductExpression( - val first: AsmExpression, - val second: AsmExpression + private val algebra: RingOperations, + first: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (first.tryEvaluate() ?: return@algebra null) * (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() first.invoke(gen) @@ -58,9 +76,14 @@ internal class AsmProductExpression( } internal class AsmConstProductExpression( - val expr: AsmExpression, - val const: Number + private val algebra: SpaceOperations, + expr: AsmExpression, + private val const: Number ) : AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() gen.visitNumberConstant(const) @@ -75,9 +98,17 @@ internal class AsmConstProductExpression( } internal class AsmDivExpression( - val expr: AsmExpression, - val second: AsmExpression + private val algebra: FieldOperations, + expr: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val expr: AsmExpression = expr.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (expr.tryEvaluate() ?: return@algebra null) / (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() expr.invoke(gen) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt index bdbab2b42..f8b4afd5b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,5 +1,6 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space @@ -17,22 +18,19 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun asmSpace( - algebra: Space, - block: AsmExpressionSpace.() -> AsmExpression -): Expression { - val expression = AsmExpressionSpace(algebra).block() +inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpression): Expression { + val expression = i.block().optimize() val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) expression.invoke(ctx) return ctx.generate() } +inline fun asmSpace( + algebra: Space, + block: AsmExpressionSpace.() -> AsmExpression +): Expression = asm(AsmExpressionSpace(algebra), algebra, block) + inline fun asmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression -): Expression { - val expression = AsmExpressionField(algebra).block() - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) - expression.invoke(ctx) - return ctx.generate() -} +): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt new file mode 100644 index 000000000..bb7d0476d --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt @@ -0,0 +1,6 @@ +package scientifik.kmath.expressions + +fun AsmExpression.optimize(): AsmExpression { + val a = tryEvaluate() + return if (a == null) this else AsmConstantExpression(a) +} From c576e46020b9a10ce2b6d3769032cc57e6522bbc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:46:00 +0700 Subject: [PATCH 15/37] Minor refactor --- .../kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt | 3 ++- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index da788372b..8147bcd5c 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -2,6 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke open class AsmExpressionSpace(private val space: Space) : Space>, ExpressionSpace> { @@ -21,7 +22,7 @@ class AsmExpressionField(private val field: Field) : ExpressionField get() = const(this.field.one) - override fun number(value: Number): AsmExpression = const(field.run { one * value }) + override fun number(value: Number): AsmExpression = const(field { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(field, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index dbc2eac1e..0304a665f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -3,6 +3,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke internal class VariableExpression(val name: String, val default: T? = null) : Expression { override fun invoke(arguments: Map): T = @@ -57,7 +58,7 @@ open class FunctionalExpressionField( override val one: Expression get() = const(this.field.one) - override fun number(value: Number): Expression = const(field.run { one * value }) + override fun number(value: Number): Expression = const(field { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) override fun divide(a: Expression, b: Expression): Expression = DivExpression(field, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) From 8f1cf0179a95345c3d9e0bd5323eb0b320f4f9dc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:48:44 +0700 Subject: [PATCH 16/37] Minor refactor --- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 0304a665f..7b5a93465 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -51,9 +51,9 @@ open class FunctionalExpressionSpace(val space: Space) : Space): Expression = arg - this } -open class FunctionalExpressionField( - val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field) { +open class FunctionalExpressionField(val field: Field) : + ExpressionField>, + FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) From 65370f93fb0f425d9c9ed0bc6bb5cbd9a049e47b Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 23:18:08 +0700 Subject: [PATCH 17/37] Make algebra and constants protected, fix tests --- .../scientifik/kmath/expressions/AsmExpressions.kt | 4 ++-- .../kotlin/scientifik/kmath/expressions/AsmTest.kt | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index cc861d363..fc7788589 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -3,8 +3,8 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.* abstract class AsmCompiledExpression internal constructor( - @JvmField private val algebra: Algebra, - @JvmField private val constants: MutableList + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: MutableList ) : Expression { abstract override fun invoke(arguments: Map): T } diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 4370029f6..437fd158f 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -31,7 +31,7 @@ class AsmTest { @Test fun testSum() = testDoubleExpressionValue( 25.0, - AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")), + AsmSumExpression(RealField, AsmConstantExpression(1.0), AsmVariableExpression("x")), mapOf("x" to 24.0) ) @@ -45,28 +45,28 @@ class AsmTest { @Test fun testDiv(): Unit = testDoubleExpressionValue( 0.5, - AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), + AsmDivExpression(RealField, AsmConstantExpression(1.0), AsmConstantExpression(2.0)), mapOf() ) @Test fun testProduct(): Unit = testDoubleExpressionValue( 25.0, - AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), + AsmProductExpression(RealField,AsmVariableExpression("x"), AsmVariableExpression("x")), mapOf("x" to 5.0) ) @Test fun testCProduct(): Unit = testDoubleExpressionValue( 25.0, - AsmConstProductExpression(AsmVariableExpression("x"), 5.0), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) ) @Test fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( 25.0, - AsmConstProductExpression(AsmVariableExpression("x"), 5f), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5f), mapOf("x" to 5.0) ) @@ -83,7 +83,7 @@ class AsmTest { @Test fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( 0.0, - AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), CustomZero), mapOf("x" to 5.0) ) From 33a519c10b10a39d99aad40c210354f6e4909d40 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 9 Jun 2020 22:22:15 +0700 Subject: [PATCH 18/37] Apply new interfaces structure to ASM Expression Field/Space --- .../kmath/expressions/AsmExpressionSpaces.kt | 11 +++++++---- .../kmath/expressions/FunctionalExpressions.kt | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index 8147bcd5c..38ed00605 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -4,8 +4,9 @@ import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space import scientifik.kmath.operations.invoke -open class AsmExpressionSpace(private val space: Space) : Space>, - ExpressionSpace> { +open class AsmExpressionSpace(private val space: Space) : + Space>, + ExpressionContext> { override val zero: AsmExpression = AsmConstantExpression(space.zero) override fun const(value: T): AsmExpression = AsmConstantExpression(value) override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) @@ -17,12 +18,14 @@ open class AsmExpressionSpace(private val space: Space) : Space): AsmExpression = arg - this } -class AsmExpressionField(private val field: Field) : ExpressionField>, +class AsmExpressionField(private val field: Field) : + ExpressionContext>, + Field>, AsmExpressionSpace(field) { override val one: AsmExpression get() = const(this.field.one) - override fun number(value: Number): AsmExpression = const(field { one * value }) + fun number(value: Number): AsmExpression = const(field { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(field, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 4f38e3a71..97002f664 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -39,7 +39,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space -) : Space>, ExpressionContext> { +) : Space>, ExpressionContext> { override val zero: Expression = ConstantExpression(space.zero) @@ -61,12 +61,12 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { +) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - fun const(value: Double): Expression = const(field.run { one*value}) + fun number(value: Number): Expression = const(field.run { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) From 1b6a0a13d8308504d7df6b8673b9c321961d518f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 10 Jun 2020 00:44:56 +0700 Subject: [PATCH 19/37] Completely rework Expression API to expose direct unaryOperation and binaryOperation, improve ASM API accordingly --- .../kmath/expressions/AsmExpressionSpaces.kt | 38 ------ .../kmath/expressions/AsmExpressions.kt | 123 ------------------ .../kmath/expressions/Optimization.kt | 6 - .../expressions/asm/AsmExpressionSpaces.kt | 74 +++++++++++ .../kmath/expressions/asm/AsmExpressions.kt | 106 +++++++++++++++ .../{ => asm}/AsmGenerationContext.kt | 24 ++-- .../kmath/expressions/{ => asm}/Builders.kt | 14 +- .../expressions/{ => asm}/MethodVisitors.kt | 2 +- .../kmath/expressions/asm/Optimization.kt | 9 ++ .../scientifik/kmath/expressions/AsmTest.kt | 97 +++----------- .../expressions/FunctionalExpressions.kt | 114 +++++++++------- 11 files changed, 301 insertions(+), 306 deletions(-) delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/AsmGenerationContext.kt (92%) rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/Builders.kt (69%) rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/MethodVisitors.kt (94%) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt deleted file mode 100644 index 38ed00605..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ /dev/null @@ -1,38 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke - -open class AsmExpressionSpace(private val space: Space) : - Space>, - ExpressionContext> { - override val zero: AsmExpression = AsmConstantExpression(space.zero) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(space, a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this -} - -class AsmExpressionField(private val field: Field) : - ExpressionContext>, - Field>, - AsmExpressionSpace(field) { - override val one: AsmExpression - get() = const(this.field.one) - - fun number(value: Number): AsmExpression = const(field { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmProductExpression(field, a, b) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(field, a, b) - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt deleted file mode 100644 index fc7788589..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ /dev/null @@ -1,123 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.* - -abstract class AsmCompiledExpression internal constructor( - @JvmField protected val algebra: Algebra, - @JvmField protected val constants: MutableList -) : Expression { - abstract override fun invoke(arguments: Map): T -} - -interface AsmExpression { - fun tryEvaluate(): T? = null - fun invoke(gen: AsmGenerationContext) -} - -internal class AsmVariableExpression(val name: String, val default: T? = null) : - AsmExpression { - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) -} - -internal class AsmConstantExpression(val value: T) : - AsmExpression { - override fun tryEvaluate(): T = value - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) -} - -internal class AsmSumExpression( - private val algebra: SpaceOperations, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (first.tryEvaluate() ?: return@algebra null) + (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - first.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "add", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmProductExpression( - private val algebra: RingOperations, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (first.tryEvaluate() ?: return@algebra null) * (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - first.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.RING_OPERATIONS_CLASS, - method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmConstProductExpression( - private val algebra: SpaceOperations, - expr: AsmExpression, - private val const: Number -) : AsmExpression { - private val expr: AsmExpression = expr.optimize() - - override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - gen.visitNumberConstant(const) - expr.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmDivExpression( - private val algebra: FieldOperations, - expr: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val expr: AsmExpression = expr.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (expr.tryEvaluate() ?: return@algebra null) / (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - expr.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.FIELD_OPERATIONS_CLASS, - method = "divide", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt deleted file mode 100644 index bb7d0476d..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt +++ /dev/null @@ -1,6 +0,0 @@ -package scientifik.kmath.expressions - -fun AsmExpression.optimize(): AsmExpression { - val a = tryEvaluate() - return if (a == null) this else AsmConstantExpression(a) -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt new file mode 100644 index 000000000..17b6dc023 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -0,0 +1,74 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.operations.* + +open class AsmExpressionAlgebra(val algebra: Algebra) : + Algebra>, + ExpressionContext> { + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) +} + +open class AsmExpressionSpace( + val space: Space +) : AsmExpressionAlgebra(space), Space> { + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override val zero: AsmExpression = AsmConstantExpression(space.zero) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) + operator fun AsmExpression.plus(arg: T) = this + const(arg) + operator fun AsmExpression.minus(arg: T) = this - const(arg) + operator fun T.plus(arg: AsmExpression) = arg + this + operator fun T.minus(arg: AsmExpression) = arg - this +} + +open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { + override val one: AsmExpression + get() = const(this.ring.one) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + fun number(value: Number): AsmExpression = const(ring { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) + + operator fun AsmExpression.times(arg: T) = this * const(arg) + operator fun T.times(arg: AsmExpression) = arg * this +} + +open class AsmExpressionField(private val field: Field) : + AsmExpressionRing(field), + Field> { + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + + operator fun AsmExpression.div(arg: T) = this / const(arg) + operator fun T.div(arg: AsmExpression) = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt new file mode 100644 index 000000000..d7d655d6e --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -0,0 +1,106 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke + +interface AsmExpression { + fun tryEvaluate(): T? = null + fun invoke(gen: AsmGenerationContext) +} + +internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : + AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = context { + unaryOperation( + name, + expr.tryEvaluate() ?: return@context null + ) + } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitStringConstant(name) + expr.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.ALGEBRA_CLASS, + method = "unaryOperation", + descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmBinaryOperation( + private val context: Algebra, + private val name: String, + first: AsmExpression, + second: AsmExpression +) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = context { + binaryOperation( + name, + first.tryEvaluate() ?: return@context null, + second.tryEvaluate() ?: return@context null + ) + } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitStringConstant(name) + first.invoke(gen) + second.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.ALGEBRA_CLASS, + method = "binaryOperation", + descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) +} + +internal class AsmConstantExpression(private val value: T) : AsmExpression { + override fun tryEvaluate(): T = value + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) +} + +internal class AsmConstProductExpression(private val context: Space, expr: AsmExpression, private val const: Number) : + AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitNumberConstant(const) + expr.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + method = "multiply", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal abstract class FunctionalCompiledExpression internal constructor( + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: MutableList +) : Expression { + abstract override fun invoke(arguments: Map): T +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt similarity index 92% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index cedd5c0fd..3e47da5e3 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label @@ -33,15 +33,15 @@ class AsmGenerationContext( private val invokeMethodVisitor: MethodVisitor private val invokeL0: Label private lateinit var invokeL1: Label - private var generatedInstance: AsmCompiledExpression? = null + private var generatedInstance: FunctionalCompiledExpression? = null init { asmCompiledClassWriter.visit( Opcodes.V1_8, Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, slashesClassName, - "L$ASM_COMPILED_EXPRESSION_CLASS;", - ASM_COMPILED_EXPRESSION_CLASS, + "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, arrayOf() ) @@ -58,7 +58,7 @@ class AsmGenerationContext( visitMethodInsn( Opcodes.INVOKESPECIAL, - ASM_COMPILED_EXPRESSION_CLASS, + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", false @@ -103,7 +103,7 @@ class AsmGenerationContext( @PublishedApi @Suppress("UNCHECKED_CAST") - internal fun generate(): AsmCompiledExpression { + internal fun generate(): FunctionalCompiledExpression { generatedInstance?.let { return it } invokeMethodVisitor.run { @@ -170,7 +170,7 @@ class AsmGenerationContext( .defineClass(className, asmCompiledClassWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants) as AsmCompiledExpression + .newInstance(algebra, constants) as FunctionalCompiledExpression generatedInstance = new return new @@ -245,7 +245,7 @@ class AsmGenerationContext( invokeMethodVisitor.visitFieldInsn( Opcodes.GETFIELD, - ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" ) invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) @@ -259,6 +259,10 @@ class AsmGenerationContext( private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) + internal fun visitStringConstant(string: String) { + invokeMethodVisitor.visitLdcInsn(string) + } + internal companion object { private val SIGNATURE_LETTERS = mapOf( java.lang.Byte::class.java to "B", @@ -269,15 +273,13 @@ class AsmGenerationContext( java.lang.Double::class.java to "D" ) - internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" internal const val LIST_CLASS = "java/util/List" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" internal const val STRING_CLASS = "java/lang/String" - internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" - internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" internal const val NUMBER_CLASS = "java/lang/Number" } } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt similarity index 69% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index f8b4afd5b..d55555567 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,7 +1,9 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space @PublishedApi @@ -25,11 +27,21 @@ inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpre return ctx.generate() } +inline fun asmAlgebra( + algebra: Algebra, + block: AsmExpressionAlgebra.() -> AsmExpression +): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) + inline fun asmSpace( algebra: Space, block: AsmExpressionSpace.() -> AsmExpression ): Expression = asm(AsmExpressionSpace(algebra), algebra, block) +inline fun asmRing( + algebra: Ring, + block: AsmExpressionRing.() -> AsmExpression +): Expression = asm(AsmExpressionRing(algebra), algebra, block) + inline fun asmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt similarity index 94% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt index 9cdb0672f..9f697ab6d 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt new file mode 100644 index 000000000..db57a690c --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt @@ -0,0 +1,9 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.asm.AsmConstantExpression +import scientifik.kmath.expressions.asm.AsmExpression + +fun AsmExpression.optimize(): AsmExpression { + val a = tryEvaluate() + return if (a == null) this else AsmConstantExpression(a) +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 437fd158f..55c240cef 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -1,103 +1,40 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra +import scientifik.kmath.expressions.asm.AsmExpression +import scientifik.kmath.expressions.asm.AsmExpressionField +import scientifik.kmath.expressions.asm.asmField import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals class AsmTest { - private fun testExpressionValue( - expectedValue: T, - expr: AsmExpression, - arguments: Map, - algebra: Algebra, - clazz: Class<*> - ): Unit = assertEquals( - expectedValue, AsmGenerationContext(clazz, algebra, "TestAsmCompiled") - .also(expr::invoke) - .generate() - .invoke(arguments) - ) - - @Suppress("UNCHECKED_CAST") - private fun testDoubleExpressionValue( - expectedValue: Double, - expr: AsmExpression, - arguments: Map, - algebra: Algebra = RealField, - clazz: Class = java.lang.Double::class.java as Class - ): Unit = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + private fun testDoubleExpression( + expected: Double?, + arguments: Map = emptyMap(), + block: AsmExpressionField.() -> AsmExpression + ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) @Test - fun testSum() = testDoubleExpressionValue( - 25.0, - AsmSumExpression(RealField, AsmConstantExpression(1.0), AsmVariableExpression("x")), - mapOf("x" to 24.0) - ) + fun testConstantsSum() = testDoubleExpression(16.0) { const(8.0) + 8.0 } @Test - fun testConst(): Unit = testDoubleExpressionValue( - 123.0, - AsmConstantExpression(123.0), - mapOf() - ) + fun testVarsSum() = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } @Test - fun testDiv(): Unit = testDoubleExpressionValue( - 0.5, - AsmDivExpression(RealField, AsmConstantExpression(1.0), AsmConstantExpression(2.0)), - mapOf() - ) + fun testProduct() = testDoubleExpression(24.0) { const(4.0) * const(6.0) } @Test - fun testProduct(): Unit = testDoubleExpressionValue( - 25.0, - AsmProductExpression(RealField,AsmVariableExpression("x"), AsmVariableExpression("x")), - mapOf("x" to 5.0) - ) + fun testConstantProduct() = testDoubleExpression(984.0) { const(8.0) * 123 } @Test - fun testCProduct(): Unit = testDoubleExpressionValue( - 25.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5.0), - mapOf("x" to 5.0) - ) + fun testSubtraction() = testDoubleExpression(2.0) { const(4.0) - 2.0 } @Test - fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( - 25.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5f), - mapOf("x" to 5.0) - ) - - object CustomZero : Number() { - override fun toByte(): Byte = 0 - override fun toChar(): Char = 0.toChar() - override fun toDouble(): Double = 0.0 - override fun toFloat(): Float = 0f - override fun toInt(): Int = 0 - override fun toLong(): Long = 0L - override fun toShort(): Short = 0 - } + fun testDivision() = testDoubleExpression(64.0) { const(128.0) / 2 } @Test - fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( - 0.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), CustomZero), - mapOf("x" to 5.0) - ) + fun testDirectCall() = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } - @Test - fun testVar(): Unit = testDoubleExpressionValue( - 10000.0, - AsmVariableExpression("x"), - mapOf("x" to 10000.0) - ) - - @Test - fun testVarWithDefault(): Unit = testDoubleExpressionValue( - 10000.0, - AsmVariableExpression("x", 10000.0), - mapOf() - ) +// @Test +// fun testSine() = testDoubleExpression(0.0) { unaryOperation("sin", const(PI)) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 97002f664..b36ea8b52 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -1,80 +1,102 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.* -internal class VariableExpression(val name: String, val default: T? = null) : Expression { +internal class FunctionalUnaryOperation(val context: Algebra, val name: String, val expr: Expression) : + Expression { + override fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) +} + +internal class FunctionalBinaryOperation( + val context: Algebra, + val name: String, + val first: Expression, + val second: Expression +) : Expression { + override fun invoke(arguments: Map): T = + context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments)) +} + +internal class FunctionalVariableExpression(val name: String, val default: T? = null) : Expression { override fun invoke(arguments: Map): T = arguments[name] ?: default ?: error("Parameter not found: $name") } -internal class ConstantExpression(val value: T) : Expression { +internal class FunctionalConstantExpression(val value: T) : Expression { override fun invoke(arguments: Map): T = value } -internal class SumExpression( - val context: Space, - val first: Expression, - val second: Expression -) : Expression { - override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) -} - -internal class ProductExpression(val context: Ring, val first: Expression, val second: Expression) : - Expression { - override fun invoke(arguments: Map): T = - context.multiply(first.invoke(arguments), second.invoke(arguments)) -} - -internal class ConstProductExpession(val context: Space, val expr: Expression, val const: Number) : +internal class FunctionalConstProductExpression(val context: Space, val expr: Expression, val const: Number) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } -internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : - Expression { - override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) +open class FunctionalExpressionAlgebra(val algebra: Algebra) : + Algebra>, + ExpressionContext> { + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) + + override fun const(value: T): Expression = FunctionalConstantExpression(value) + override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } -open class FunctionalExpressionSpace( - val space: Space -) : Space>, ExpressionContext> { +open class FunctionalExpressionSpace(val space: Space) : + FunctionalExpressionAlgebra(space), + Space> { + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) - override val zero: Expression = ConstantExpression(space.zero) + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) - override fun const(value: T): Expression = ConstantExpression(value) - - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) + override val zero: Expression = FunctionalConstantExpression(space.zero) + override fun add(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) operator fun Expression.plus(arg: T) = this + const(arg) operator fun Expression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: Expression) = arg + this operator fun T.minus(arg: Expression) = arg - this } -open class FunctionalExpressionField( - val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { - +open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { override val one: Expression - get() = const(this.field.one) + get() = const(this.ring.one) - fun number(value: Number): Expression = const(field.run { one * value }) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) - override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) - override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + fun number(value: Number): Expression = const(ring { one * value }) + + override fun multiply(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - operator fun T.times(arg: Expression) = arg * this +} + +open class FunctionalExpressionField(val field: Field) : + FunctionalExpressionRing(field), + Field> { + + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) + + override fun divide(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + + operator fun Expression.div(arg: T) = this / const(arg) operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file +} From a0453da4b34706cd7853f49cd4b7de6fa69aa179 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Wed, 10 Jun 2020 08:57:17 +0700 Subject: [PATCH 20/37] Refactor, replace constants List with Array, create specification of named functions --- kmath-asm/build.gradle.kts | 1 + .../expressions/asm/AsmExpressionSpaces.kt | 16 ++--- .../kmath/expressions/asm/AsmExpressions.kt | 59 +++++++++++++++---- .../expressions/asm/AsmGenerationContext.kt | 17 +++--- .../kmath/expressions/asm/Builders.kt | 12 ++-- .../kmath/expressions/asm/Optimization.kt | 6 +- .../kmath/expressions/Expression.kt | 14 ++--- .../expressions/FunctionalExpressions.kt | 16 ++--- 8 files changed, 89 insertions(+), 52 deletions(-) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts index 4104349f8..3b004eb8e 100644 --- a/kmath-asm/build.gradle.kts +++ b/kmath-asm/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { api(project(path = ":kmath-core")) implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 17b6dc023..24260b871 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -31,10 +31,10 @@ open class AsmExpressionSpace( AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T) = this + const(arg) - operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this - operator fun T.minus(arg: AsmExpression) = arg - this + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this } open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { @@ -52,8 +52,8 @@ open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace< override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmExpression.times(arg: T) = this * const(arg) - operator fun T.times(arg: AsmExpression) = arg * this + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this } open class AsmExpressionField(private val field: Field) : @@ -69,6 +69,6 @@ open class AsmExpressionField(private val field: Field) : override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.div(arg: AsmExpression) = arg / this + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt index d7d655d6e..662e1279e 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -4,28 +4,55 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Space import scientifik.kmath.operations.invoke +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.jvmName interface AsmExpression { fun tryEvaluate(): T? = null fun invoke(gen: AsmGenerationContext) } +internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { + context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + ?: return false + + return true +} + +internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { + context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + ?: return false + + val owner = context::class.jvmName.replace('.', '/') + + val sig = buildString { + append('(') + repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + append(')') + append("L${AsmGenerationContext.OBJECT_CLASS};") + } + + visitAlgebraOperation(owner = owner, method = name, descriptor = sig) + + return true +} + internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : AsmExpression { private val expr: AsmExpression = expr.optimize() - - override fun tryEvaluate(): T? = context { - unaryOperation( - name, - expr.tryEvaluate() ?: return@context null - ) - } + override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - gen.visitStringConstant(name) + + if (!hasSpecific(context, name, 1)) + gen.visitStringConstant(name) + expr.invoke(gen) + if (gen.tryInvokeSpecific(context, name, 1)) + return + gen.visitAlgebraOperation( owner = AsmGenerationContext.ALGEBRA_CLASS, method = "unaryOperation", @@ -55,10 +82,16 @@ internal class AsmBinaryOperation( override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - gen.visitStringConstant(name) + + if (!hasSpecific(context, name, 1)) + gen.visitStringConstant(name) + first.invoke(gen) second.invoke(gen) + if (gen.tryInvokeSpecific(context, name, 1)) + return + gen.visitAlgebraOperation( owner = AsmGenerationContext.ALGEBRA_CLASS, method = "binaryOperation", @@ -79,7 +112,11 @@ internal class AsmConstantExpression(private val value: T) : AsmExpression override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } -internal class AsmConstProductExpression(private val context: Space, expr: AsmExpression, private val const: Number) : +internal class AsmConstProductExpression( + private val context: Space, + expr: AsmExpression, + private val const: Number +) : AsmExpression { private val expr: AsmExpression = expr.optimize() @@ -100,7 +137,7 @@ internal class AsmConstProductExpression(private val context: Space, expr: internal abstract class FunctionalCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, - @JvmField protected val constants: MutableList + @JvmField protected val constants: Array ) : Expression { abstract override fun invoke(arguments: Map): T } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index 3e47da5e3..0375b7fc4 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -46,7 +46,7 @@ class AsmGenerationContext( ) asmCompiledClassWriter.run { - visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", null, null).run { val thisVar = 0 val algebraVar = 1 val constantsVar = 2 @@ -60,7 +60,7 @@ class AsmGenerationContext( Opcodes.INVOKESPECIAL, FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "", - "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", false ) @@ -80,7 +80,7 @@ class AsmGenerationContext( algebraVar ) - visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitLocalVariable("constants", "[L$OBJECT_CLASS;", null, l0, l2, constantsVar) visitMaxs(3, 3) visitEnd() } @@ -170,7 +170,7 @@ class AsmGenerationContext( .defineClass(className, asmCompiledClassWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants) as FunctionalCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression generatedInstance = new return new @@ -184,9 +184,9 @@ class AsmGenerationContext( invokeMethodVisitor.run { visitLoadThis() - visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "[L$OBJECT_CLASS;") visitLdcOrIConstInsn(idx) - visitMethodInsn(Opcodes.INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + visitInsn(Opcodes.AALOAD) invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type) } } @@ -273,8 +273,9 @@ class AsmGenerationContext( java.lang.Double::class.java to "D" ) - internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" - internal const val LIST_CLASS = "java/util/List" + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = + "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" + internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index d55555567..663bd55ba 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,10 +1,8 @@ package scientifik.kmath.expressions.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.operations.* @PublishedApi internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { @@ -20,7 +18,11 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpression): Expression { +inline fun >> asm( + i: E, + algebra: Algebra, + block: E.() -> AsmExpression +): Expression { val expression = i.block().optimize() val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) expression.invoke(ctx) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt index db57a690c..bcd182dc3 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt @@ -1,9 +1,7 @@ package scientifik.kmath.expressions.asm -import scientifik.kmath.expressions.asm.AsmConstantExpression -import scientifik.kmath.expressions.asm.AsmExpression - -fun AsmExpression.optimize(): AsmExpression { +@PublishedApi +internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index 73745f78f..8d8bd3a7a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -26,11 +26,9 @@ interface ExpressionContext : Algebra { fun const(value: T): E } -fun ExpressionContext.produce(node: SyntaxTreeNode): E { - return when (node) { - is NumberNode -> error("Single number nodes are not supported") - is SingularNode -> variable(node.value) - is UnaryNode -> unaryOperation(node.operation, produce(node.value)) - is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) - } -} \ No newline at end of file +fun ExpressionContext.produce(node: SyntaxTreeNode): E = when (node) { + is NumberNode -> error("Single number nodes are not supported") + is SingularNode -> variable(node.value) + is UnaryNode -> unaryOperation(node.operation, produce(node.value)) + is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index b36ea8b52..d20a5dbbf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -59,10 +59,10 @@ open class FunctionalExpressionSpace(val space: Space) : FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this + operator fun Expression.plus(arg: T): Expression = this + const(arg) + operator fun Expression.minus(arg: T): Expression = this - const(arg) + operator fun T.plus(arg: Expression): Expression = arg + this + operator fun T.minus(arg: Expression): Expression = arg - this } open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { @@ -80,8 +80,8 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - operator fun Expression.times(arg: T) = this * const(arg) - operator fun T.times(arg: Expression) = arg * this + operator fun Expression.times(arg: T): Expression = this * const(arg) + operator fun T.times(arg: Expression): Expression = arg * this } open class FunctionalExpressionField(val field: Field) : @@ -97,6 +97,6 @@ open class FunctionalExpressionField(val field: Field) : override fun divide(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) - operator fun Expression.div(arg: T) = this / const(arg) - operator fun T.div(arg: Expression) = arg / this + operator fun Expression.div(arg: T): Expression = this / const(arg) + operator fun T.div(arg: Expression): Expression = arg / this } From 927aa589ad4277d8a95aea2c402128a8183e59e7 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 08:49:38 +0700 Subject: [PATCH 21/37] Add missing override modifiers --- .../scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt | 2 +- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 24260b871..78f6ee351 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -47,7 +47,7 @@ open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace< override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, operation, left, right) - fun number(value: Number): AsmExpression = const(ring { one * value }) + override fun number(value: Number): AsmExpression = const(ring { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index d20a5dbbf..ce9b4756c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -75,7 +75,7 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - fun number(value: Number): Expression = const(ring { one * value }) + override fun number(value: Number): Expression = const(ring { one * value }) override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) From e6f97c532b645d019fd76dfe7fec99fac5d55899 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 08:50:37 +0700 Subject: [PATCH 22/37] Minor refactor: replace space property with field --- .../scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 78f6ee351..232710d6c 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -67,7 +67,7 @@ open class AsmExpressionField(private val field: Field) : AsmBinaryOperation(algebra, operation, left, right) override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + AsmBinaryOperation(field, FieldOperations.DIV_OPERATION, a, b) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.div(arg: AsmExpression): AsmExpression = arg / this From 89fae390134b1c5ff27c4864b9ef094356d49036 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 09:27:23 +0700 Subject: [PATCH 23/37] Improve tests --- .../expressions/asm/AsmGenerationContext.kt | 3 ++- .../scientifik/kmath/expressions/AsmTest.kt | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index 0375b7fc4..92663ba8f 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -215,7 +215,7 @@ class AsmGenerationContext( visitLoadAnyFromConstants(value, c) } - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { maxStack += 2 visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) @@ -241,6 +241,7 @@ class AsmGenerationContext( } internal fun visitLoadAlgebra() { + maxStack++ invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) invokeMethodVisitor.visitFieldInsn( diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 55c240cef..93c52faac 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -3,6 +3,8 @@ package scientifik.kmath.expressions import scientifik.kmath.expressions.asm.AsmExpression import scientifik.kmath.expressions.asm.AsmExpressionField import scientifik.kmath.expressions.asm.asmField +import scientifik.kmath.expressions.asm.asmRing +import scientifik.kmath.operations.IntRing import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -15,26 +17,29 @@ class AsmTest { ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) @Test - fun testConstantsSum() = testDoubleExpression(16.0) { const(8.0) + 8.0 } + fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } @Test - fun testVarsSum() = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } + fun testVarsSum(): Unit = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } @Test - fun testProduct() = testDoubleExpression(24.0) { const(4.0) * const(6.0) } + fun testProduct(): Unit = testDoubleExpression(24.0) { const(4.0) * const(6.0) } @Test - fun testConstantProduct() = testDoubleExpression(984.0) { const(8.0) * 123 } + fun testConstantProduct(): Unit = testDoubleExpression(984.0) { const(8.0) * 123 } @Test - fun testSubtraction() = testDoubleExpression(2.0) { const(4.0) - 2.0 } + fun testVarsConstantProductVar(): Unit = testDoubleExpression(984.0, mapOf("x" to 8.0)) { variable("x") * 123 } @Test - fun testDivision() = testDoubleExpression(64.0) { const(128.0) / 2 } + fun testSubtraction(): Unit = testDoubleExpression(2.0) { const(4.0) - 2.0 } @Test - fun testDirectCall() = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } + fun testDivision(): Unit = testDoubleExpression(64.0) { const(128.0) / 2 } -// @Test -// fun testSine() = testDoubleExpression(0.0) { unaryOperation("sin", const(PI)) } + @Test + fun testDirectUnaryCall(): Unit = testDoubleExpression(64.0) { unaryOperation("+", const(64.0)) } + + @Test + fun testDirectBinaryCall(): Unit = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } } From 39907a1da239d0eba4f7affbb3438ddcfeb89b1f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 12 Jun 2020 19:50:28 +0700 Subject: [PATCH 24/37] Add name adapting of *, +, / --- .../kmath/expressions/asm/AsmExpressions.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt index 662e1279e..70f1fc28e 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -12,15 +12,21 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } +internal val methodNameAdapters = mapOf("+" to "add", "*" to "multiply", "/" to "divide") + internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { - context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } ?: return false return true } internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } ?: return false val owner = context::class.jvmName.replace('.', '/') @@ -32,7 +38,7 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, append("L${AsmGenerationContext.OBJECT_CLASS};") } - visitAlgebraOperation(owner = owner, method = name, descriptor = sig) + visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) return true } From b902f3980a7021a5bdbb0ddc36e45e545d750a95 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 12 Jun 2020 22:10:56 +0700 Subject: [PATCH 25/37] Fix issues found after merge --- .../expressions/asm/AsmExpressionSpaces.kt | 4 +-- .../kmath/expressions/asm/Builders.kt | 12 ++++---- .../scientifik/kmath/expressions/AsmTest.kt | 6 ++-- .../scientifik/kmath/expressions/Builders.kt | 30 +++++++++++++++++++ .../expressions/FunctionalExpressions.kt | 2 +- settings.gradle.kts | 1 + 6 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 232710d6c..0f8f685eb 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -1,11 +1,11 @@ package scientifik.kmath.expressions.asm -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* open class AsmExpressionAlgebra(val algebra: Algebra) : Algebra>, - ExpressionContext> { + ExpressionAlgebra> { override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index 663bd55ba..c56adb767 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,7 +1,7 @@ package scientifik.kmath.expressions.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* @PublishedApi @@ -18,7 +18,7 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun >> asm( +inline fun >> asm( i: E, algebra: Algebra, block: E.() -> AsmExpression @@ -29,22 +29,22 @@ inline fun >> asm( return ctx.generate() } -inline fun asmAlgebra( +inline fun buildAsmAlgebra( algebra: Algebra, block: AsmExpressionAlgebra.() -> AsmExpression ): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) -inline fun asmSpace( +inline fun buildAsmSpace( algebra: Space, block: AsmExpressionSpace.() -> AsmExpression ): Expression = asm(AsmExpressionSpace(algebra), algebra, block) -inline fun asmRing( +inline fun buildAsmRing( algebra: Ring, block: AsmExpressionRing.() -> AsmExpression ): Expression = asm(AsmExpressionRing(algebra), algebra, block) -inline fun asmField( +inline fun buildAsmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression ): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 93c52faac..034ba09c8 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -2,9 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.expressions.asm.AsmExpression import scientifik.kmath.expressions.asm.AsmExpressionField -import scientifik.kmath.expressions.asm.asmField -import scientifik.kmath.expressions.asm.asmRing -import scientifik.kmath.operations.IntRing +import scientifik.kmath.expressions.asm.buildAsmField import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -14,7 +12,7 @@ class AsmTest { expected: Double?, arguments: Map = emptyMap(), block: AsmExpressionField.() -> AsmExpression - ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) + ): Unit = assertEquals(expected = expected, actual = buildAsmField(RealField, block)(arguments)) @Test fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt new file mode 100644 index 000000000..b2e4d8a3d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -0,0 +1,30 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.Space + +/** + * Create a functional expression on this [Algebra] + */ +fun Algebra.buildExpression(block: FunctionalExpressionAlgebra.() -> Expression): Expression = + FunctionalExpressionAlgebra(this).run(block) + +/** + * Create a functional expression on this [Space] + */ +fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).run(block) + +/** + * Create a functional expression on this [Ring] + */ +fun Ring.buildExpression(block: FunctionalExpressionRing.() -> Expression): Expression = + FunctionalExpressionRing(this).run(block) + +/** + * Create a functional expression on this [Field] + */ +fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index ce9b4756c..cbbdb99f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -33,7 +33,7 @@ internal class FunctionalConstProductExpression(val context: Space, val ex open class FunctionalExpressionAlgebra(val algebra: Algebra) : Algebra>, - ExpressionContext> { + ExpressionAlgebra> { override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) diff --git a/settings.gradle.kts b/settings.gradle.kts index 465ecfca8..2b2633060 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,5 +45,6 @@ include( ":kmath-for-real", ":kmath-geometry", ":kmath-ast", + ":kmath-asm", ":examples" ) From 3ec1f7b5f15ada3527902c448f0e9517213a4354 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 02:26:12 +0700 Subject: [PATCH 26/37] Merge kmath-asm and kmath-ast modules, make all the ExpressionAlgebras concise and consistent, implement new-styled builders both for ASM and F. expressions --- kmath-asm/build.gradle.kts | 10 -- .../expressions/asm/AsmExpressionSpaces.kt | 74 --------------- .../kmath/expressions/asm/Builders.kt | 50 ---------- .../scientifik/kmath/expressions/AsmTest.kt | 43 --------- kmath-ast/build.gradle.kts | 28 ++++-- .../scientifik/kmath}/asm/AsmExpressions.kt | 92 +++++++++++++++++-- .../kmath}/asm/AsmGenerationContext.kt | 10 +- .../kotlin/scientifik/kmath/asm/Builders.kt | 53 +++++++++++ .../scientifik/kmath}/asm/MethodVisitors.kt | 2 +- .../scientifik/kmath}/asm/Optimization.kt | 2 +- .../kmath/ast/{parser.kt => Parser.kt} | 0 .../kotlin/scientifik/kmath/ast/asm.kt | 23 ----- .../kotlin/scietifik/kmath/ast/AsmTest.kt | 18 ++++ .../kotlin/scietifik/kmath/ast/ParserTest.kt | 10 +- .../scientifik/kmath/expressions/Builders.kt | 13 +-- .../expressions/FunctionalExpressions.kt | 51 +++++----- settings.gradle.kts | 1 - 17 files changed, 220 insertions(+), 260 deletions(-) delete mode 100644 kmath-asm/build.gradle.kts delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt delete mode 100644 kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/AsmExpressions.kt (52%) rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/AsmGenerationContext.kt (96%) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/MethodVisitors.kt (94%) rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/Optimization.kt (80%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/{parser.kt => Parser.kt} (100%) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts deleted file mode 100644 index 3b004eb8e..000000000 --- a/kmath-asm/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("scientifik.jvm") -} - -dependencies { - api(project(path = ":kmath-core")) - implementation("org.ow2.asm:asm:8.0.1") - implementation("org.ow2.asm:asm-commons:8.0.1") - implementation(kotlin("reflect")) -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt deleted file mode 100644 index 0f8f685eb..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ /dev/null @@ -1,74 +0,0 @@ -package scientifik.kmath.expressions.asm - -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.* - -open class AsmExpressionAlgebra(val algebra: Algebra) : - Algebra>, - ExpressionAlgebra> { - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) -} - -open class AsmExpressionSpace( - val space: Space -) : AsmExpressionAlgebra(space), Space> { - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override val zero: AsmExpression = AsmConstantExpression(space.zero) - - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this -} - -open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { - override val one: AsmExpression - get() = const(this.ring.one) - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): AsmExpression = const(ring { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this -} - -open class AsmExpressionField(private val field: Field) : - AsmExpressionRing(field), - Field> { - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(field, FieldOperations.DIV_OPERATION, a, b) - - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt deleted file mode 100644 index c56adb767..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ /dev/null @@ -1,50 +0,0 @@ -package scientifik.kmath.expressions.asm - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.* - -@PublishedApi -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - - -inline fun >> asm( - i: E, - algebra: Algebra, - block: E.() -> AsmExpression -): Expression { - val expression = i.block().optimize() - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) - expression.invoke(ctx) - return ctx.generate() -} - -inline fun buildAsmAlgebra( - algebra: Algebra, - block: AsmExpressionAlgebra.() -> AsmExpression -): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) - -inline fun buildAsmSpace( - algebra: Space, - block: AsmExpressionSpace.() -> AsmExpression -): Expression = asm(AsmExpressionSpace(algebra), algebra, block) - -inline fun buildAsmRing( - algebra: Ring, - block: AsmExpressionRing.() -> AsmExpression -): Expression = asm(AsmExpressionRing(algebra), algebra, block) - -inline fun buildAsmField( - algebra: Field, - block: AsmExpressionField.() -> AsmExpression -): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt deleted file mode 100644 index 034ba09c8..000000000 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.expressions.asm.AsmExpression -import scientifik.kmath.expressions.asm.AsmExpressionField -import scientifik.kmath.expressions.asm.buildAsmField -import scientifik.kmath.operations.RealField -import kotlin.test.Test -import kotlin.test.assertEquals - -class AsmTest { - private fun testDoubleExpression( - expected: Double?, - arguments: Map = emptyMap(), - block: AsmExpressionField.() -> AsmExpression - ): Unit = assertEquals(expected = expected, actual = buildAsmField(RealField, block)(arguments)) - - @Test - fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } - - @Test - fun testVarsSum(): Unit = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } - - @Test - fun testProduct(): Unit = testDoubleExpression(24.0) { const(4.0) * const(6.0) } - - @Test - fun testConstantProduct(): Unit = testDoubleExpression(984.0) { const(8.0) * 123 } - - @Test - fun testVarsConstantProductVar(): Unit = testDoubleExpression(984.0, mapOf("x" to 8.0)) { variable("x") * 123 } - - @Test - fun testSubtraction(): Unit = testDoubleExpression(2.0) { const(4.0) - 2.0 } - - @Test - fun testDivision(): Unit = testDoubleExpression(64.0) { const(128.0) / 2 } - - @Test - fun testDirectUnaryCall(): Unit = testDoubleExpression(64.0) { unaryOperation("+", const(64.0)) } - - @Test - fun testDirectBinaryCall(): Unit = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } -} diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 88540e7b8..deb6a28ee 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,10 +1,5 @@ -plugins { - id("scientifik.mpp") -} - -repositories{ - maven("https://dl.bintray.com/hotkeytlt/maven") -} +plugins { id("scientifik.mpp") } +repositories { maven("https://dl.bintray.com/hotkeytlt/maven") } kotlin.sourceSets { commonMain { @@ -13,9 +8,22 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } } -} \ No newline at end of file + + jvmTest { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-junit5")) + } + } +} + +tasks.withType { useJUnitPlatform() } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt similarity index 52% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 70f1fc28e..75b5f26d0 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,9 +1,8 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke +import scientifik.kmath.expressions.ExpressionAlgebra +import scientifik.kmath.operations.* import kotlin.reflect.full.memberFunctions import kotlin.reflect.jvm.jvmName @@ -109,11 +108,13 @@ internal class AsmBinaryOperation( } } -internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { +internal class AsmVariableExpression(private val name: String, private val default: T? = null) : + AsmExpression { override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } -internal class AsmConstantExpression(private val value: T) : AsmExpression { +internal class AsmConstantExpression(private val value: T) : + AsmExpression { override fun tryEvaluate(): T = value override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } @@ -141,9 +142,88 @@ internal class AsmConstProductExpression( } } +internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : AsmExpression { + override fun tryEvaluate(): T? = context.number(value) + + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) +} + internal abstract class FunctionalCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, @JvmField protected val constants: Array ) : Expression { abstract override fun invoke(arguments: Map): T } + +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { + val algebra: A + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) +} + +open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmExpression + get() = const(algebra.zero) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this +} + +open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmExpression + get() = const(algebra.one) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun number(value: Number): AsmExpression = const(algebra { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) + + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this +} + +open class AsmExpressionField(override val algebra: A) : + AsmExpressionRing(algebra), + Field> where A : Field, A : NumericAlgebra { + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) + + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this +} + diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt similarity index 96% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index 92663ba8f..d9dba746d 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label @@ -15,7 +15,8 @@ class AsmGenerationContext( internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } - private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + private val classLoader: ClassLoader = + ClassLoader(javaClass.classLoader) @Suppress("PrivatePropertyName") private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') @@ -236,7 +237,8 @@ class AsmGenerationContext( } visitLdcInsn(name) - visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitMethodInsn(Opcodes.INVOKEINTERFACE, + MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) visitCastToT() } @@ -275,7 +277,7 @@ class AsmGenerationContext( ) internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" + "scientifik/kmath/asm/FunctionalCompiledExpression" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt new file mode 100644 index 000000000..67870e07e --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt @@ -0,0 +1,53 @@ +package scientifik.kmath.asm + +import scientifik.kmath.ast.MST +import scientifik.kmath.ast.evaluate +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.* + +@PublishedApi +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + +inline fun AsmExpression.compile(algebra: Algebra): Expression { + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) + invoke(ctx) + return ctx.generate() +} + +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + block: E.() -> AsmExpression +): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) + +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + ast: MST +): Expression = asm(expressionAlgebra) { evaluate(ast) } + +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = + AsmExpressionSpace(this).let { it.block().compile(it.algebra) } + +inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = + asmSpace { evaluate(ast) } + +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = + AsmExpressionRing(this).let { it.block().compile(it.algebra) } + +inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = + asmRing { evaluate(ast) } + +inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = + AsmExpressionField(this).let { it.block().compile(it.algebra) } + +inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = + asmRing { evaluate(ast) } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt similarity index 94% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt index 9f697ab6d..42565b6ba 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt similarity index 80% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index bcd182dc3..e821bd206 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm @PublishedApi internal fun AsmExpression.optimize(): AsmExpression { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/Parser.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/Parser.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt deleted file mode 100644 index c01648fb0..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt +++ /dev/null @@ -1,23 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra - -//TODO stubs for asm generation - -interface AsmExpression - -interface AsmExpressionAlgebra> : NumericAlgebra> { - val algebra: A -} - -fun AsmExpression.compile(): Expression = TODO() - -//TODO add converter for functional expressions - -inline fun > A.asm( - block: AsmExpressionAlgebra.() -> AsmExpression -): Expression = TODO() - -inline fun > A.asm(ast: MST): Expression = asm { evaluate(ast) } \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt new file mode 100644 index 000000000..73058face --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -0,0 +1,18 @@ +package scietifik.kmath.ast + +import org.junit.jupiter.api.Test +import scientifik.kmath.asm.asmField +import scientifik.kmath.ast.parseMath +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.Complex +import scientifik.kmath.operations.ComplexField +import kotlin.test.assertEquals + +class AsmTest { + @Test + fun parsedExpression() { + val mst = "2+2*(2+2)".parseMath() + val res = ComplexField.asmField(mst)() + assertEquals(Complex(10.0, 0.0), res) + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 6849d24b8..ddac07786 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,17 +1,17 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.parseMath import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField +import kotlin.test.assertEquals -internal class ParserTest{ +internal class ParserTest { @Test - fun parsedExpression(){ + fun parsedExpression() { val mst = "2+2*(2+2)".parseMath() val res = ComplexField.evaluate(mst) - assertEquals(Complex(10.0,0.0), res) + assertEquals(Complex(10.0, 0.0), res) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index b2e4d8a3d..cdd6f695b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,30 +1,23 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space -/** - * Create a functional expression on this [Algebra] - */ -fun Algebra.buildExpression(block: FunctionalExpressionAlgebra.() -> Expression): Expression = - FunctionalExpressionAlgebra(this).run(block) - /** * Create a functional expression on this [Space] */ -fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = +fun Space.buildExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = FunctionalExpressionSpace(this).run(block) /** * Create a functional expression on this [Ring] */ -fun Ring.buildExpression(block: FunctionalExpressionRing.() -> Expression): Expression = +fun Ring.buildExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = FunctionalExpressionRing(this).run(block) /** * Create a functional expression on this [Field] */ -fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = +fun Field.buildExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index cbbdb99f5..88d8c87b7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -26,48 +26,55 @@ internal class FunctionalConstantExpression(val value: T) : Expression { override fun invoke(arguments: Map): T = value } -internal class FunctionalConstProductExpression(val context: Space, val expr: Expression, val const: Number) : +internal class FunctionalConstProductExpression( + val context: Space, + val expr: Expression, + val const: Number +) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } -open class FunctionalExpressionAlgebra(val algebra: Algebra) : - Algebra>, - ExpressionAlgebra> { - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) +interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { + val algebra: A override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + override fun const(value: T): Expression = FunctionalConstantExpression(value) override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } -open class FunctionalExpressionSpace(val space: Space) : - FunctionalExpressionAlgebra(space), - Space> { - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) +open class FunctionalExpressionSpace(override val algebra: A) : FunctionalExpressionAlgebra, + Space> where A : Space { + override val zero: Expression + get() = const(algebra.zero) override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - override val zero: Expression = FunctionalConstantExpression(space.zero) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) override fun add(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: Expression, k: Number): Expression = + FunctionalConstProductExpression(algebra, a, k) - override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) operator fun Expression.plus(arg: T): Expression = this + const(arg) operator fun Expression.minus(arg: T): Expression = this - const(arg) operator fun T.plus(arg: Expression): Expression = arg + this operator fun T.minus(arg: Expression): Expression = arg - this } -open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { +open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), + Ring> where A : Ring, A : NumericAlgebra { override val one: Expression - get() = const(this.ring.one) + get() = const(algebra.one) override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) @@ -75,18 +82,18 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - override fun number(value: Number): Expression = const(ring { one * value }) + override fun number(value: Number): Expression = const(algebra { one * value }) override fun multiply(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) + FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this } -open class FunctionalExpressionField(val field: Field) : - FunctionalExpressionRing(field), - Field> { +open class FunctionalExpressionField(override val algebra: A) : + FunctionalExpressionRing(algebra), + Field> where A : Field, A : NumericAlgebra { override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) @@ -95,7 +102,7 @@ open class FunctionalExpressionField(val field: Field) : FunctionalBinaryOperation(algebra, operation, left, right) override fun divide(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b2633060..465ecfca8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,6 +45,5 @@ include( ":kmath-for-real", ":kmath-geometry", ":kmath-ast", - ":kmath-asm", ":examples" ) From 36ad1fcf589e0f8e9b4701f463360d7d66c80c41 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:44:54 +0700 Subject: [PATCH 27/37] Minor refactor and document --- .../scientifik/kmath/asm/AsmExpressions.kt | 101 +++++++++++++----- .../kmath/asm/AsmGenerationContext.kt | 12 ++- .../expressions/FunctionalExpressions.kt | 84 ++++++++++----- 3 files changed, 141 insertions(+), 56 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 75b5f26d0..ef579278a 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -142,7 +142,8 @@ internal class AsmConstProductExpression( } } -internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : AsmExpression { +internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : + AsmExpression { override fun tryEvaluate(): T? = context.number(value) override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) @@ -155,75 +156,121 @@ internal abstract class FunctionalCompiledExpression internal constructor( abstract override fun invoke(arguments: Map): T } +/** + * A context class for [AsmExpression] construction. + */ interface AsmExpressionAlgebra> : NumericAlgebra>, ExpressionAlgebra> { + /** + * The algebra to provide for AsmExpressions built. + */ val algebra: A + + /** + * Builds an AsmExpression to wrap a number. + */ + override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) + + /** + * Builds an AsmExpression of constant expression which does not depend on arguments. + */ + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + + /** + * Builds an AsmExpression to access a variable. + */ + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + + /** + * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. + */ override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, operation, left, right) + /** + * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. + */ override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) - - override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) } +/** + * A context class for [AsmExpression] construction for [Space] algebras. + */ open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, Space> where A : Space, A : NumericAlgebra { override val zero: AsmExpression get() = const(algebra.zero) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - + /** + * Builds an AsmExpression of addition of two another expressions. + */ override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + /** + * Builds an AsmExpression of multiplication of expression by number. + */ override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) } +/** + * A context class for [AsmExpression] construction for [Ring] algebras. + */ open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), Ring> where A : Ring, A : NumericAlgebra { override val one: AsmExpression get() = const(algebra.one) - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): AsmExpression = const(algebra { one * value }) - + /** + * Builds an AsmExpression of multiplication of two expressions. + */ override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) + + override fun number(value: Number): AsmExpression = super.number(value) } +/** + * A context class for [AsmExpression] construction for [Field] algebras. + */ open class AsmExpressionField(override val algebra: A) : AsmExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - + /** + * Builds an AsmExpression of division an expression by another one. + */ override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) + + override fun number(value: Number): AsmExpression = super.number(value) +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index d9dba746d..69a2b6c85 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -6,6 +6,15 @@ import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra +/** + * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java + * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new + * class. + * + * @param T the type of AsmExpression to unwrap. + * @param algebra the algebra the applied AsmExpressions use. + * @param className the unique class name of new loaded class. + */ class AsmGenerationContext( classOfT: Class<*>, private val algebra: Algebra, @@ -29,8 +38,7 @@ class AsmGenerationContext( private val invokeArgumentsVar: Int = 1 private var maxStack: Int = 0 private val constants: MutableList = mutableListOf() - private val asmCompiledClassWriter: ClassWriter = - ClassWriter(0) + private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) private val invokeMethodVisitor: MethodVisitor private val invokeL0: Label private lateinit var invokeL1: Label diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 88d8c87b7..c3f36a814 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -28,40 +28,61 @@ internal class FunctionalConstantExpression(val value: T) : Expression { internal class FunctionalConstProductExpression( val context: Space, - val expr: Expression, + private val expr: Expression, val const: Number -) : - Expression { +) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } +/** + * A context class for [Expression] construction. + */ interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { + /** + * The algebra to provide for Expressions built. + */ val algebra: A + /** + * Builds an Expression of constant expression which does not depend on arguments. + */ + override fun const(value: T): Expression = FunctionalConstantExpression(value) + + /** + * Builds an Expression to access a variable. + */ + override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) + + /** + * Builds an Expression of dynamic call of binary operation [operation] on [left] and [right]. + */ override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) + /** + * Builds an Expression of dynamic call of unary operation with name [operation] on [arg]. + */ override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) - - override fun const(value: T): Expression = FunctionalConstantExpression(value) - override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } +/** + * A context class for [Expression] construction for [Space] algebras. + */ open class FunctionalExpressionSpace(override val algebra: A) : FunctionalExpressionAlgebra, Space> where A : Space { override val zero: Expression get() = const(algebra.zero) - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - + /** + * Builds an Expression of addition of two another expressions. + */ override fun add(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + /** + * Builds an Expression of multiplication of expression by number. + */ override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(algebra, a, k) @@ -69,6 +90,12 @@ open class FunctionalExpressionSpace(override val algebra: A) : Functional operator fun Expression.minus(arg: T): Expression = this - const(arg) operator fun T.plus(arg: Expression): Expression = arg + this operator fun T.minus(arg: Expression): Expression = arg - this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), @@ -76,34 +103,37 @@ open class FunctionalExpressionRing(override val algebra: A) : FunctionalE override val one: Expression get() = const(algebra.one) - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): Expression = const(algebra { one * value }) - + /** + * Builds an Expression of multiplication of two expressions. + */ override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } open class FunctionalExpressionField(override val algebra: A) : FunctionalExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { - - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - + /** + * Builds an Expression of division an expression by another one. + */ override fun divide(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } From fec8c7f9d19118f38188d804fd8248aeb8fbafe8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:50:21 +0700 Subject: [PATCH 28/37] Minor refactor and encapsulation --- .../jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt | 6 ++++-- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index ef579278a..9e6efb66b 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -11,7 +11,7 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } -internal val methodNameAdapters = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name @@ -137,7 +137,9 @@ internal class AsmConstProductExpression( gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" + + "L${AsmGenerationContext.NUMBER_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" ) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index c3f36a814..a441a80c0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -2,7 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.* -internal class FunctionalUnaryOperation(val context: Algebra, val name: String, val expr: Expression) : +internal class FunctionalUnaryOperation(val context: Algebra, val name: String, private val expr: Expression) : Expression { override fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) } From 1582fde0915332681884d6c50f5bf1f516ff5c6a Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:51:42 +0700 Subject: [PATCH 29/37] Replace JUnit @Test with kotlin-test @Test --- kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt | 2 +- kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 73058face..c92524b5d 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,12 +1,12 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Test import scientifik.kmath.asm.asmField import scientifik.kmath.ast.parseMath import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.assertEquals +import kotlin.test.Test class AsmTest { @Test diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index ddac07786..06546302e 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,11 +1,11 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Test import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.parseMath import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.assertEquals +import kotlin.test.Test internal class ParserTest { @Test From 834d1e1397e76943adab584c46a7526c47073da3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:53:25 +0700 Subject: [PATCH 30/37] Move specific optimization functions to Optimization --- .../scientifik/kmath/asm/AsmExpressions.kt | 31 ---------------- .../scientifik/kmath/asm/Optimization.kt | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 9e6efb66b..c4039da81 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -11,37 +11,6 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } -private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") - -internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name - - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } - ?: return false - - return true -} - -internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name - - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } - ?: return false - - val owner = context::class.jvmName.replace('.', '/') - - val sig = buildString { - append('(') - repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } - append(')') - append("L${AsmGenerationContext.OBJECT_CLASS};") - } - - visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) - - return true -} - internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : AsmExpression { private val expr: AsmExpression = expr.optimize() diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index e821bd206..4e094fdd8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,5 +1,40 @@ package scientifik.kmath.asm +import scientifik.kmath.operations.Algebra +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.jvmName + +private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") + +internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + ?: return false + + return true +} + +internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + ?: return false + + val owner = context::class.jvmName.replace('.', '/') + + val sig = buildString { + append('(') + repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + append(')') + append("L${AsmGenerationContext.OBJECT_CLASS};") + } + + visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) + + return true +} + @PublishedApi internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() From 223d238c435f6e7b34d5ae778ec35b64603f0e81 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:53:54 +0700 Subject: [PATCH 31/37] Encapsulate MethodVisitor extensions --- .../jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt index 42565b6ba..aec5a123d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { +internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { -1 -> visitInsn(ICONST_M1) 0 -> visitInsn(ICONST_0) 1 -> visitInsn(ICONST_1) @@ -14,13 +14,13 @@ fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { else -> visitLdcInsn(value) } -fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { +internal fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { 0.0 -> visitInsn(DCONST_0) 1.0 -> visitInsn(DCONST_1) else -> visitLdcInsn(value) } -fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { +internal fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) 2f -> visitInsn(FCONST_2) From e65d1e43cf64a27a04bc20b3245814be372008b9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 16:16:57 +0700 Subject: [PATCH 32/37] Write tests --- .../kmath/ast/asm/TestAsmAlgebras.kt | 75 +++++++++++++++++++ .../kmath/ast/asm/TestAsmExpressions.kt | 21 ++++++ 2 files changed, 96 insertions(+) create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt new file mode 100644 index 000000000..07f895c40 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt @@ -0,0 +1,75 @@ +package scietifik.kmath.ast.asm + +import scientifik.kmath.asm.asmField +import scientifik.kmath.asm.asmRing +import scientifik.kmath.asm.asmSpace +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.ByteRing +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestAsmAlgebras { + @Test + fun space() { + val res = ByteRing.asmSpace { + binaryOperation( + "+", + + unaryOperation( + "+", + 3.toByte() - (2.toByte() + (multiply( + add(const(1), const(1)), + 2 + ) + 1.toByte()) * 3.toByte() - 1.toByte()) + ), + + number(1) + ) + variable("x") + zero + }("x" to 2.toByte()) + + assertEquals(16, res) + } + + @Test + fun ring() { + val res = ByteRing.asmRing { + binaryOperation( + "+", + + unaryOperation( + "+", + (3.toByte() - (2.toByte() + (multiply( + add(const(1), const(1)), + 2 + ) + 1.toByte()))) * 3.0 - 1.toByte() + ), + + number(1) + ) * const(2) + }() + + assertEquals(24, res) + } + + @Test + fun field() { + val res = RealField.asmField { + divide(binaryOperation( + "+", + + unaryOperation( + "+", + (3.0 - (2.0 + (multiply( + add(const(1.0), const(1.0)), + 2 + ) + 1.0))) * 3 - 1.0 + ), + + number(1) + ) / 2, const(2.0)) * one + }() + + assertEquals(3.0, res) + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt new file mode 100644 index 000000000..2839b8a25 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt @@ -0,0 +1,21 @@ +package scietifik.kmath.ast.asm + +import scientifik.kmath.asm.asmField +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestAsmExpressions { + @Test + fun testUnaryOperationInvocation() { + val res = RealField.asmField { unaryOperation("+", variable("x")) }("x" to 2.0) + assertEquals(2.0, res) + } + + @Test + fun testConstProductInvocation() { + val res = RealField.asmField { variable("x") * 2 }("x" to 2.0) + assertEquals(4.0, res) + } +} From f9835979ea22069b0f3f94ec436b665ecb508554 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 16:48:45 +0700 Subject: [PATCH 33/37] Fix specification bug --- .../scientifik/kmath/asm/AsmExpressions.kt | 4 ++-- .../kmath/asm/AsmGenerationContext.kt | 17 +++++++++++++---- .../kotlin/scientifik/kmath/asm/Optimization.kt | 17 +++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index c4039da81..83f3b7754 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -57,13 +57,13 @@ internal class AsmBinaryOperation( override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - if (!hasSpecific(context, name, 1)) + if (!hasSpecific(context, name, 2)) gen.visitStringConstant(name) first.invoke(gen) second.invoke(gen) - if (gen.tryInvokeSpecific(context, name, 1)) + if (gen.tryInvokeSpecific(context, name, 2)) return gen.visitAlgebraOperation( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index 69a2b6c85..c10e56ba8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -4,6 +4,7 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes +import scientifik.kmath.asm.AsmGenerationContext.ClassLoader import scientifik.kmath.operations.Algebra /** @@ -245,8 +246,10 @@ class AsmGenerationContext( } visitLdcInsn(name) - visitMethodInsn(Opcodes.INVOKEINTERFACE, - MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitMethodInsn( + Opcodes.INVOKEINTERFACE, + MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true + ) visitCastToT() } @@ -262,9 +265,15 @@ class AsmGenerationContext( invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) } - internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { + internal fun visitAlgebraOperation( + owner: String, + method: String, + descriptor: String, + opcode: Int = Opcodes.INVOKEINTERFACE, + isInterface: Boolean = true + ) { maxStack++ - invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, method, descriptor, true) + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) visitCastToT() } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index 4e094fdd8..3198fa8ec 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,15 +1,14 @@ package scientifik.kmath.asm +import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra -import kotlin.reflect.full.memberFunctions -import kotlin.reflect.jvm.jvmName private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + context::class.java.methods.find { it.name == aName && it.parameters.size == arity } ?: return false return true @@ -18,10 +17,10 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + context::class.java.methods.find { it.name == aName && it.parameters.size == arity } ?: return false - val owner = context::class.jvmName.replace('.', '/') + val owner = context::class.java.name.replace('.', '/') val sig = buildString { append('(') @@ -30,7 +29,13 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, append("L${AsmGenerationContext.OBJECT_CLASS};") } - visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) + visitAlgebraOperation( + owner = owner, + method = aName, + descriptor = sig, + opcode = Opcodes.INVOKEVIRTUAL, + isInterface = false + ) return true } From e91f6470d3d6fd4d21574b4006977ae7b4be9b2e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 17:07:22 +0700 Subject: [PATCH 34/37] Implement constants inlining --- .../kmath/asm/AsmGenerationContext.kt | 33 ++++++++++++------- .../scientifik/kmath/asm/Optimization.kt | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index c10e56ba8..cc7874e37 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -17,7 +17,7 @@ import scientifik.kmath.operations.Algebra * @param className the unique class name of new loaded class. */ class AsmGenerationContext( - classOfT: Class<*>, + private val classOfT: Class<*>, private val algebra: Algebra, private val className: String ) { @@ -186,7 +186,15 @@ class AsmGenerationContext( return new } - internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + internal fun visitLoadFromConstants(value: T) { + if (classOfT in INLINABLE_NUMBERS) { + visitNumberConstant(value as Number) + visitCastToT() + return + } + + visitLoadAnyFromConstants(value as Any, T_CLASS) + } private fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex @@ -207,7 +215,6 @@ class AsmGenerationContext( maxStack++ val clazz = value.javaClass val c = clazz.name.replace('.', '/') - val sigLetter = SIGNATURE_LETTERS[clazz] if (sigLetter != null) { @@ -284,14 +291,18 @@ class AsmGenerationContext( } internal companion object { - private val SIGNATURE_LETTERS = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) + private val SIGNATURE_LETTERS by lazy { + mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) + } + + private val INLINABLE_NUMBERS by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/asm/FunctionalCompiledExpression" diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index 3198fa8ec..51048c77c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -2,6 +2,7 @@ package scientifik.kmath.asm import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.ByteRing private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") From af410dde70af10e7531809dd18862d5eaca8a204 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:18:40 +0700 Subject: [PATCH 35/37] Apply the suggested changes --- .../kmath/asm/{Builders.kt => AsmBuilders.kt} | 5 ++- .../scientifik/kmath/asm/AsmExpressions.kt | 43 +++++++++++++------ .../kmath/asm/AsmGenerationContext.kt | 5 ++- .../asm/{ => internal}/MethodVisitors.kt | 2 +- .../kmath/asm/{ => internal}/Optimization.kt | 6 ++- 5 files changed, 42 insertions(+), 19 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{Builders.kt => AsmBuilders.kt} (94%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/MethodVisitors.kt (95%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/Optimization.kt (88%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt similarity index 94% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt index 67870e07e..14456a426 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -18,9 +18,10 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String return buildName(expression, collision + 1) } -inline fun AsmExpression.compile(algebra: Algebra): Expression { +@PublishedApi +internal inline fun AsmExpression.compile(algebra: Algebra): Expression { val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) - invoke(ctx) + compile(ctx) return ctx.generate() } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 83f3b7754..cd1815f7c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,14 +1,31 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.hasSpecific +import scientifik.kmath.asm.internal.optimize +import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.expressions.Expression import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* -import kotlin.reflect.full.memberFunctions -import kotlin.reflect.jvm.jvmName +/** + * A function declaration that could be compiled to [AsmGenerationContext]. + * + * @param T the type the stored function returns. + */ interface AsmExpression { + /** + * Tries to evaluate this function without its variables. This method is intended for optimization. + * + * @return `null` if the function depends on its variables, the value if the function is a constant. + */ fun tryEvaluate(): T? = null - fun invoke(gen: AsmGenerationContext) + + /** + * Compiles this declaration. + * + * @param gen the target [AsmGenerationContext]. + */ + fun compile(gen: AsmGenerationContext) } internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : @@ -16,13 +33,13 @@ internal class AsmUnaryOperation(private val context: Algebra, private val private val expr: AsmExpression = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() if (!hasSpecific(context, name, 1)) gen.visitStringConstant(name) - expr.invoke(gen) + expr.compile(gen) if (gen.tryInvokeSpecific(context, name, 1)) return @@ -54,14 +71,14 @@ internal class AsmBinaryOperation( ) } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() if (!hasSpecific(context, name, 2)) gen.visitStringConstant(name) - first.invoke(gen) - second.invoke(gen) + first.compile(gen) + second.compile(gen) if (gen.tryInvokeSpecific(context, name, 2)) return @@ -79,13 +96,13 @@ internal class AsmBinaryOperation( internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : AsmExpression { override fun tryEvaluate(): T = value - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmConstProductExpression( @@ -98,10 +115,10 @@ internal class AsmConstProductExpression( override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() gen.visitNumberConstant(const) - expr.invoke(gen) + expr.compile(gen) gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, @@ -117,7 +134,7 @@ internal class AsmNumberExpression(private val context: NumericAlgebra, pr AsmExpression { override fun tryEvaluate(): T? = context.number(value) - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index cc7874e37..be8da3eff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -5,6 +5,9 @@ import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmGenerationContext.ClassLoader +import scientifik.kmath.asm.internal.visitLdcOrDConstInsn +import scientifik.kmath.asm.internal.visitLdcOrFConstInsn +import scientifik.kmath.asm.internal.visitLdcOrIConstInsn import scientifik.kmath.operations.Algebra /** @@ -16,7 +19,7 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -class AsmGenerationContext( +class AsmGenerationContext @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt similarity index 95% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index aec5a123d..356de4765 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt similarity index 88% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt index 51048c77c..cd41ff07c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt @@ -1,8 +1,10 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes +import scientifik.kmath.asm.AsmConstantExpression +import scientifik.kmath.asm.AsmExpression +import scientifik.kmath.asm.AsmGenerationContext import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.ByteRing private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") From 28cecde05bb16f35db04f31bd2585538e563eed1 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:24:10 +0700 Subject: [PATCH 36/37] Fix compilation problems found after merge --- kmath-ast/build.gradle.kts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 65a1ac12c..9764fccc3 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.mpp") } -repositories{ +repositories { maven("https://dl.bintray.com/hotkeytlt/maven") } @@ -14,13 +14,18 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } } - jsMain{ - dependencies{ + + jsMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } } From d3d348620acb55feea6d915eca4523789594bba3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:30:39 +0700 Subject: [PATCH 37/37] Rename AsmExpression to AsmNode, encapsulate AsmGenerationContext, make AsmNode (ex-AsmExpression) an abstract class instead of interface --- .../scientifik/kmath/asm/AsmBuilders.kt | 16 +-- .../scientifik/kmath/asm/AsmExpressions.kt | 109 +++++++++--------- .../{ => internal}/AsmGenerationContext.kt | 13 +-- .../kmath/asm/internal/Optimization.kt | 5 +- 4 files changed, 72 insertions(+), 71 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/AsmGenerationContext.kt (96%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt index 14456a426..69e49f8b6 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -1,12 +1,13 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmGenerationContext import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.* @PublishedApi -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { +internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" try { @@ -19,15 +20,16 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } @PublishedApi -internal inline fun AsmExpression.compile(algebra: Algebra): Expression { - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) +internal inline fun AsmNode.compile(algebra: Algebra): Expression { + val ctx = + AsmGenerationContext(T::class.java, algebra, buildName(this)) compile(ctx) return ctx.generate() } inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, - block: E.() -> AsmExpression + block: E.() -> AsmNode ): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) inline fun , E : AsmExpressionAlgebra> A.asm( @@ -35,19 +37,19 @@ inline fun , E : AsmExpressionAlgebra> A. ast: MST ): Expression = asm(expressionAlgebra) { evaluate(ast) } -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmNode): Expression where A : NumericAlgebra, A : Space = AsmExpressionSpace(this).let { it.block().compile(it.algebra) } inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = asmSpace { evaluate(ast) } -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmNode): Expression where A : NumericAlgebra, A : Ring = AsmExpressionRing(this).let { it.block().compile(it.algebra) } inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = asmRing { evaluate(ast) } -inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = +inline fun A.asmField(block: AsmExpressionField.() -> AsmNode): Expression where A : NumericAlgebra, A : Field = AsmExpressionField(this).let { it.block().compile(it.algebra) } inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index cd1815f7c..8f16a2710 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,5 +1,6 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmGenerationContext import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -12,25 +13,26 @@ import scientifik.kmath.operations.* * * @param T the type the stored function returns. */ -interface AsmExpression { +abstract class AsmNode internal constructor() { /** * Tries to evaluate this function without its variables. This method is intended for optimization. * * @return `null` if the function depends on its variables, the value if the function is a constant. */ - fun tryEvaluate(): T? = null + internal open fun tryEvaluate(): T? = null /** * Compiles this declaration. * * @param gen the target [AsmGenerationContext]. */ - fun compile(gen: AsmGenerationContext) + @PublishedApi + internal abstract fun compile(gen: AsmGenerationContext) } -internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : - AsmExpression { - private val expr: AsmExpression = expr.optimize() +internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : + AsmNode() { + private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } override fun compile(gen: AsmGenerationContext) { @@ -57,11 +59,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val internal class AsmBinaryOperation( private val context: Algebra, private val name: String, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() + first: AsmNode, + second: AsmNode +) : AsmNode() { + private val first: AsmNode = first.optimize() + private val second: AsmNode = second.optimize() override fun tryEvaluate(): T? = context { binaryOperation( @@ -95,23 +97,22 @@ internal class AsmBinaryOperation( } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : - AsmExpression { + AsmNode() { override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : - AsmExpression { + AsmNode() { override fun tryEvaluate(): T = value override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmConstProductExpression( private val context: Space, - expr: AsmExpression, + expr: AsmNode, private val const: Number -) : - AsmExpression { - private val expr: AsmExpression = expr.optimize() +) : AsmNode() { + private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } @@ -131,7 +132,7 @@ internal class AsmConstProductExpression( } internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : - AsmExpression { + AsmNode() { override fun tryEvaluate(): T? = context.number(value) override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) @@ -145,10 +146,10 @@ internal abstract class FunctionalCompiledExpression internal constructor( } /** - * A context class for [AsmExpression] construction. + * A context class for [AsmNode] construction. */ -interface AsmExpressionAlgebra> : NumericAlgebra>, - ExpressionAlgebra> { +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { /** * The algebra to provide for AsmExpressions built. */ @@ -157,108 +158,108 @@ interface AsmExpressionAlgebra> : NumericAlgebra = AsmNumberExpression(algebra, value) + override fun number(value: Number): AsmNode = AsmNumberExpression(algebra, value) /** * Builds an AsmExpression of constant expression which does not depend on arguments. */ - override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun const(value: T): AsmNode = AsmConstantExpression(value) /** * Builds an AsmExpression to access a variable. */ - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun variable(name: String, default: T?): AsmNode = AsmVariableExpression(name, default) /** * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. */ - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = AsmBinaryOperation(algebra, operation, left, right) /** * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. */ - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = AsmUnaryOperation(algebra, operation, arg) } /** - * A context class for [AsmExpression] construction for [Space] algebras. + * A context class for [AsmNode] construction for [Space] algebras. */ open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, - Space> where A : Space, A : NumericAlgebra { - override val zero: AsmExpression + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmNode get() = const(algebra.zero) /** * Builds an AsmExpression of addition of two another expressions. */ - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun add(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an AsmExpression of multiplication of expression by number. */ - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + override fun multiply(a: AsmNode, k: Number): AsmNode = AsmConstProductExpression(algebra, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this + operator fun AsmNode.plus(arg: T): AsmNode = this + const(arg) + operator fun AsmNode.minus(arg: T): AsmNode = this - const(arg) + operator fun T.plus(arg: AsmNode): AsmNode = arg + this + operator fun T.minus(arg: AsmNode): AsmNode = arg - this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) } /** - * A context class for [AsmExpression] construction for [Ring] algebras. + * A context class for [AsmNode] construction for [Ring] algebras. */ open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), - Ring> where A : Ring, A : NumericAlgebra { - override val one: AsmExpression + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmNode get() = const(algebra.one) /** * Builds an AsmExpression of multiplication of two expressions. */ - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun multiply(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + operator fun AsmNode.times(arg: T): AsmNode = this * const(arg) + operator fun T.times(arg: AsmNode): AsmNode = arg * this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmExpression = super.number(value) + override fun number(value: Number): AsmNode = super.number(value) } /** - * A context class for [AsmExpression] construction for [Field] algebras. + * A context class for [AsmNode] construction for [Field] algebras. */ open class AsmExpressionField(override val algebra: A) : AsmExpressionRing(algebra), - Field> where A : Field, A : NumericAlgebra { + Field> where A : Field, A : NumericAlgebra { /** * Builds an AsmExpression of division an expression by another one. */ - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun divide(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this + operator fun AsmNode.div(arg: T): AsmNode = this / const(arg) + operator fun T.div(arg: AsmNode): AsmNode = arg / this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmExpression = super.number(value) + override fun number(value: Number): AsmNode = super.number(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt similarity index 96% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt index be8da3eff..fa0f06579 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt @@ -1,17 +1,15 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmGenerationContext.ClassLoader -import scientifik.kmath.asm.internal.visitLdcOrDConstInsn -import scientifik.kmath.asm.internal.visitLdcOrFConstInsn -import scientifik.kmath.asm.internal.visitLdcOrIConstInsn +import scientifik.kmath.asm.FunctionalCompiledExpression +import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader import scientifik.kmath.operations.Algebra /** - * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java + * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new * class. * @@ -19,7 +17,8 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -class AsmGenerationContext @PublishedApi internal constructor( +@PublishedApi +internal class AsmGenerationContext @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt index cd41ff07c..815f292ce 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt @@ -2,8 +2,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmExpression -import scientifik.kmath.asm.AsmGenerationContext +import scientifik.kmath.asm.AsmNode import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -44,7 +43,7 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, } @PublishedApi -internal fun AsmExpression.optimize(): AsmExpression { +internal fun AsmNode.optimize(): AsmNode { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) }