From 635aac5f30ce84516db3d3bcc1d4a065572a728c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 22:58:09 +0700 Subject: [PATCH 1/9] Refactor ex-AsmGenerationContext, introduce many bytecode utility functions to make its code readable, update compile method --- .../scientifik/kmath/asm/AsmBuilders.kt | 10 +- .../scientifik/kmath/asm/AsmExpressions.kt | 64 ++-- .../asm/internal/AsmGenerationContext.kt | 319 ------------------ .../kmath/asm/internal/AsmGenerator.kt | 311 +++++++++++++++++ .../kmath/asm/internal/ClassWriters.kt | 15 + .../kmath/asm/internal/MethodVisitors.kt | 32 +- .../kmath/asm/internal/Optimization.kt | 8 +- 7 files changed, 394 insertions(+), 365 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt 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 69e49f8b6..223278756 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerationContext +import scientifik.kmath.asm.internal.AsmGenerator import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression @@ -20,12 +20,8 @@ internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { } @PublishedApi -internal inline fun AsmNode.compile(algebra: Algebra): Expression { - val ctx = - AsmGenerationContext(T::class.java, algebra, buildName(this)) - compile(ctx) - return ctx.generate() -} +internal inline fun AsmNode.compile(algebra: Algebra): Expression = + AsmGenerator(T::class.java, algebra, buildName(this), this).getInstance() inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, 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 8f16a2710..c1f71612f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerationContext +import scientifik.kmath.asm.internal.AsmGenerator import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -9,7 +9,7 @@ import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* /** - * A function declaration that could be compiled to [AsmGenerationContext]. + * A function declaration that could be compiled to [AsmGenerator]. * * @param T the type the stored function returns. */ @@ -24,10 +24,10 @@ abstract class AsmNode internal constructor() { /** * Compiles this declaration. * - * @param gen the target [AsmGenerationContext]. + * @param gen the target [AsmGenerator]. */ @PublishedApi - internal abstract fun compile(gen: AsmGenerationContext) + internal abstract fun compile(gen: AsmGenerator) } internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : @@ -35,23 +35,23 @@ internal class AsmUnaryOperation(private val context: Algebra, private val private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() if (!hasSpecific(context, name, 1)) - gen.visitStringConstant(name) + gen.loadStringConstant(name) expr.compile(gen) if (gen.tryInvokeSpecific(context, name, 1)) return - gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.ALGEBRA_CLASS, method = "unaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.STRING_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } @@ -73,11 +73,11 @@ internal class AsmBinaryOperation( ) } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() if (!hasSpecific(context, name, 2)) - gen.visitStringConstant(name) + gen.loadStringConstant(name) first.compile(gen) second.compile(gen) @@ -85,26 +85,26 @@ internal class AsmBinaryOperation( if (gen.tryInvokeSpecific(context, name, 2)) return - gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.ALGEBRA_CLASS, method = "binaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.STRING_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmNode() { - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) + override fun compile(gen: AsmGenerator): Unit = gen.loadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : AsmNode() { override fun tryEvaluate(): T = value - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) + override fun compile(gen: AsmGenerator): Unit = gen.loadTConstant(value) } internal class AsmConstProductExpression( @@ -116,17 +116,17 @@ internal class AsmConstProductExpression( override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - gen.visitNumberConstant(const) + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() + gen.loadNumberConstant(const) expr.compile(gen) - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.NUMBER_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.OBJECT_CLASS};" + + "L${AsmGenerator.NUMBER_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } @@ -135,7 +135,7 @@ internal class AsmNumberExpression(private val context: NumericAlgebra, pr AsmNode() { override fun tryEvaluate(): T? = context.number(value) - override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) + override fun compile(gen: AsmGenerator): Unit = gen.loadNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt deleted file mode 100644 index fa0f06579..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt +++ /dev/null @@ -1,319 +0,0 @@ -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.FunctionalCompiledExpression -import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader -import scientifik.kmath.operations.Algebra - -/** - * 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. - * - * @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. - */ -@PublishedApi -internal class AsmGenerationContext @PublishedApi internal constructor( - private val 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: FunctionalCompiledExpression? = null - - init { - asmCompiledClassWriter.visit( - Opcodes.V1_8, - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, - slashesClassName, - "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, - arrayOf() - ) - - asmCompiledClassWriter.run { - visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;[L$OBJECT_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, - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, - "", - "(L$ALGEBRA_CLASS;[L$OBJECT_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$OBJECT_CLASS;", null, 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(): FunctionalCompiledExpression { - 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.toTypedArray()) as FunctionalCompiledExpression - - generatedInstance = new - return new - } - - 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 - maxStack++ - - invokeMethodVisitor.run { - visitLoadThis() - visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "[L$OBJECT_CLASS;") - visitLdcOrIConstInsn(idx) - visitInsn(Opcodes.AALOAD) - 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): Unit = 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() { - maxStack++ - invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) - - invokeMethodVisitor.visitFieldInsn( - Opcodes.GETFIELD, - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" - ) - - invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) - } - - internal fun visitAlgebraOperation( - owner: String, - method: String, - descriptor: String, - opcode: Int = Opcodes.INVOKEINTERFACE, - isInterface: Boolean = true - ) { - maxStack++ - invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) - visitCastToT() - } - - 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 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" - - 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 NUMBER_CLASS = "java/lang/Number" - } -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt new file mode 100644 index 000000000..f636fc289 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -0,0 +1,311 @@ +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.AsmNode +import scientifik.kmath.asm.FunctionalCompiledExpression +import scientifik.kmath.asm.internal.AsmGenerator.ClassLoader +import scientifik.kmath.operations.Algebra + +/** + * ASM Generator 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. + * + * @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. + */ +@Suppress("UNCHECKED_CAST") +@PublishedApi +internal class AsmGenerator @PublishedApi internal constructor( + private val classOfT: Class<*>, + private val algebra: Algebra, + private val className: String, + private val root: AsmNode +) { + 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 val constants: MutableList = mutableListOf() + private lateinit var invokeMethodVisitor: MethodVisitor + private var generatedInstance: FunctionalCompiledExpression? = null + + fun getInstance(): FunctionalCompiledExpression { + generatedInstance?.let { return it } + + val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { + visit( + Opcodes.V1_8, + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, + slashesClassName, + "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + arrayOf() + ) + + visitMethod( + access = Opcodes.ACC_PUBLIC, + name = "", + descriptor = "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", + signature = null, + exceptions = null + ) { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitLoadObjectVar(thisVar) + visitLoadObjectVar(algebraVar) + visitLoadObjectVar(constantsVar) + + visitInvokeSpecial( + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + "", + "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V" + ) + + val l1 = Label() + visitLabel(l1) + visitReturn() + 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$OBJECT_CLASS;", null, l0, l2, constantsVar) + visitMaxs(0, 3) + visitEnd() + } + + invokeMethodVisitor = visitMethod( + access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + name = "invoke", + descriptor = "(L$MAP_CLASS;)L$T_CLASS;", + signature = "(L$MAP_CLASS;)L$T_CLASS;", + exceptions = null + ) {} + + invokeMethodVisitor.run { + visitCode() + val l0 = Label() + visitLabel(l0) + root.compile(this@AsmGenerator) + visitReturnObject() + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + invokeThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + l0, + l1, + invokeArgumentsVar + ) + + visitMaxs(0, 2) + visitEnd() + } + + visitMethod( + access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + name = "invoke", + descriptor = "(L$MAP_CLASS;)L$OBJECT_CLASS;", + signature = null, + exceptions = null + ) { + val thisVar = 0 + val argumentsVar = 1 + visitCode() + val l0 = Label() + visitLabel(l0) + visitLoadObjectVar(thisVar) + visitLoadObjectVar(argumentsVar) + visitInvokeVirtual(owner = slashesClassName, name = "invoke", descriptor = "(L$MAP_CLASS;)L$T_CLASS;") + visitReturnObject() + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(0, 2) + visitEnd() + } + + visitEnd() + } + + val new = classLoader + .defineClass(className, classWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression + + generatedInstance = new + return new + } + + internal fun loadTConstant(value: T) { + if (classOfT in INLINABLE_NUMBERS) { + loadNumberConstant(value as Number) + invokeMethodVisitor.visitCheckCast(T_CLASS) + return + } + + loadConstant(value as Any, T_CLASS) + } + + private fun loadConstant(value: Any, type: String) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + + invokeMethodVisitor.run { + loadThis() + visitGetField(owner = slashesClassName, name = "constants", descriptor = "[L$OBJECT_CLASS;") + visitLdcOrIntConstant(idx) + visitGetObjectArrayElement() + invokeMethodVisitor.visitCheckCast(type) + } + } + + private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) + + internal fun loadNumberConstant(value: Number) { + val clazz = value.javaClass + val c = clazz.name.replace('.', '/') + val sigLetter = SIGNATURE_LETTERS[clazz] + + if (sigLetter != null) { + when (value) { + is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) + is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) + is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) + else -> invokeMethodVisitor.visitLdcInsn(value) + } + + invokeMethodVisitor.visitInvokeStatic(c, "valueOf", "($sigLetter)L${c};") + return + } + + loadConstant(value, c) + } + + internal fun loadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { + visitLoadObjectVar(invokeArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + loadTConstant(defaultValue) + + visitInvokeInterface( + owner = MAP_CLASS, + name = "getOrDefault", + descriptor = "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_CLASS) + return + } + + visitLdcInsn(name) + + visitInvokeInterface( + owner = MAP_CLASS, + name = "get", + descriptor = "(L$OBJECT_CLASS;)L$OBJECT_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_CLASS) + } + + internal fun loadAlgebra() { + loadThis() + + invokeMethodVisitor.visitGetField( + owner = FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + name = "algebra", + descriptor = "L$ALGEBRA_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_ALGEBRA_CLASS) + } + + internal fun invokeAlgebraOperation( + owner: String, + method: String, + descriptor: String, + opcode: Int = Opcodes.INVOKEINTERFACE, + isInterface: Boolean = true + ) { + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) + invokeMethodVisitor.visitCheckCast(T_CLASS) + } + + internal fun loadStringConstant(string: String) { + invokeMethodVisitor.visitLdcInsn(string) + } + + internal companion object { + private val SIGNATURE_LETTERS: Map, String> 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: Set> by lazy { SIGNATURE_LETTERS.keys } + + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = + "scientifik/kmath/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" + internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + internal const val STRING_CLASS = "java/lang/String" + internal const val NUMBER_CLASS = "java/lang/Number" + } +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt new file mode 100644 index 000000000..84f736ee7 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt @@ -0,0 +1,15 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.MethodVisitor + +inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) + +inline fun ClassWriter.visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array?, + block: MethodVisitor.() -> Unit +): MethodVisitor = visitMethod(access, name, descriptor, signature, exceptions).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index 356de4765..d29d786fc 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { +internal fun MethodVisitor.visitLdcOrIntConstant(value: Int): Unit = when (value) { -1 -> visitInsn(ICONST_M1) 0 -> visitInsn(ICONST_0) 1 -> visitInsn(ICONST_1) @@ -14,15 +14,41 @@ internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { else -> visitLdcInsn(value) } -internal fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { +internal fun MethodVisitor.visitLdcOrDoubleConstant(value: Double): Unit = when (value) { 0.0 -> visitInsn(DCONST_0) 1.0 -> visitInsn(DCONST_1) else -> visitLdcInsn(value) } -internal fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { +internal fun MethodVisitor.visitLdcOrFloatConstant(value: Float): Unit = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) 2f -> visitInsn(FCONST_2) else -> visitLdcInsn(value) } + +internal fun MethodVisitor.visitInvokeInterface(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKEINTERFACE, owner, name, descriptor, true) + +internal fun MethodVisitor.visitInvokeVirtual(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor, false) + +internal fun MethodVisitor.visitInvokeStatic(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKESTATIC, owner, name, descriptor, false) + +internal fun MethodVisitor.visitInvokeSpecial(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, false) + +internal fun MethodVisitor.visitCheckCast(type: String): Unit = visitTypeInsn(CHECKCAST, type) + +internal fun MethodVisitor.visitGetField(owner: String, name: String, descriptor: String): Unit = + visitFieldInsn(GETFIELD, owner, name, descriptor) + +internal fun MethodVisitor.visitLoadObjectVar(`var`: Int) { + visitVarInsn(ALOAD, `var`) +} + +internal fun MethodVisitor.visitGetObjectArrayElement(): Unit = visitInsn(AALOAD) + +internal fun MethodVisitor.visitReturn(): Unit = visitInsn(RETURN) +internal fun MethodVisitor.visitReturnObject(): Unit = visitInsn(ARETURN) 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 815f292ce..3b37f81e5 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 @@ -16,7 +16,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } -internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name context::class.java.methods.find { it.name == aName && it.parameters.size == arity } @@ -26,12 +26,12 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, val sig = buildString { append('(') - repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + repeat(arity) { append("L${AsmGenerator.OBJECT_CLASS};") } append(')') - append("L${AsmGenerationContext.OBJECT_CLASS};") + append("L${AsmGenerator.OBJECT_CLASS};") } - visitAlgebraOperation( + invokeAlgebraOperation( owner = owner, method = aName, descriptor = sig, From 4e28ad7d4e91eb26196907d5f28c618211f71a31 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:00:26 +0700 Subject: [PATCH 2/9] Minor refactor --- .../scientifik/kmath/asm/internal/AsmGenerator.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt index f636fc289..d2568eb53 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -17,7 +17,6 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -@Suppress("UNCHECKED_CAST") @PublishedApi internal class AsmGenerator @PublishedApi internal constructor( private val classOfT: Class<*>, @@ -45,6 +44,7 @@ internal class AsmGenerator @PublishedApi internal constructor( private lateinit var invokeMethodVisitor: MethodVisitor private var generatedInstance: FunctionalCompiledExpression? = null + @Suppress("UNCHECKED_CAST") fun getInstance(): FunctionalCompiledExpression { generatedInstance?.let { return it } @@ -101,15 +101,14 @@ internal class AsmGenerator @PublishedApi internal constructor( visitEnd() } - invokeMethodVisitor = visitMethod( + visitMethod( access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, name = "invoke", descriptor = "(L$MAP_CLASS;)L$T_CLASS;", signature = "(L$MAP_CLASS;)L$T_CLASS;", exceptions = null - ) {} - - invokeMethodVisitor.run { + ) { + invokeMethodVisitor = this visitCode() val l0 = Label() visitLabel(l0) @@ -121,7 +120,7 @@ internal class AsmGenerator @PublishedApi internal constructor( visitLocalVariable( "this", "L$slashesClassName;", - T_CLASS, + null, l0, l1, invokeThisVar @@ -280,9 +279,7 @@ internal class AsmGenerator @PublishedApi internal constructor( invokeMethodVisitor.visitCheckCast(T_CLASS) } - internal fun loadStringConstant(string: String) { - invokeMethodVisitor.visitLdcInsn(string) - } + internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) internal companion object { private val SIGNATURE_LETTERS: Map, String> by lazy { From a8fa38549758b5602118c85d23ffdf061fbce4ba Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:01:34 +0700 Subject: [PATCH 3/9] Rename loadFromVariables to loadVariable --- .../src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt | 2 +- .../kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 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 c1f71612f..72a53d899 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -98,7 +98,7 @@ internal class AsmBinaryOperation( internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmNode() { - override fun compile(gen: AsmGenerator): Unit = gen.loadFromVariables(name, default) + override fun compile(gen: AsmGenerator): Unit = gen.loadVariable(name, default) } internal class AsmConstantExpression(private val value: T) : diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt index d2568eb53..0c6975cd9 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -228,7 +228,7 @@ internal class AsmGenerator @PublishedApi internal constructor( loadConstant(value, c) } - internal fun loadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { + internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { visitLoadObjectVar(invokeArgumentsVar) if (defaultValue != null) { From a7302f49fffdadf91e4255fa3f65e476d51af96d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:13:26 +0700 Subject: [PATCH 4/9] Convert to expression body --- .../kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index d29d786fc..70fb3bd44 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -44,9 +44,7 @@ internal fun MethodVisitor.visitCheckCast(type: String): Unit = visitTypeInsn(CH internal fun MethodVisitor.visitGetField(owner: String, name: String, descriptor: String): Unit = visitFieldInsn(GETFIELD, owner, name, descriptor) -internal fun MethodVisitor.visitLoadObjectVar(`var`: Int) { - visitVarInsn(ALOAD, `var`) -} +internal fun MethodVisitor.visitLoadObjectVar(`var`: Int): Unit = visitVarInsn(ALOAD, `var`) internal fun MethodVisitor.visitGetObjectArrayElement(): Unit = visitInsn(AALOAD) From 521ea8bddcb906bd43b9d1ac82712f2fcb305a19 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 15 Jun 2020 17:36:30 +0700 Subject: [PATCH 5/9] Rename ClassWriters.kt to be consistent with local code style, rename AsmBuilders.kt to asm.kt, rename AsmNode back to AsmExpression, rename AsmGenerator to AsmBuilder --- .../scientifik/kmath/asm/AsmBuilders.kt | 52 ------ .../scientifik/kmath/asm/AsmExpressions.kt | 148 +++++++++--------- .../kotlin/scientifik/kmath/asm/asm.kt | 40 +++-- .../{AsmGenerator.kt => AsmBuilder.kt} | 12 +- .../{ClassWriters.kt => classWriters.kt} | 0 .../kmath/asm/internal/optimization.kt | 10 +- 6 files changed, 108 insertions(+), 154 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{AsmGenerator.kt => AsmBuilder.kt} (96%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{ClassWriters.kt => classWriters.kt} (100%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt deleted file mode 100644 index 223278756..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ /dev/null @@ -1,52 +0,0 @@ -package scientifik.kmath.asm - -import scientifik.kmath.asm.internal.AsmGenerator -import scientifik.kmath.ast.MST -import scientifik.kmath.ast.evaluate -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.* - -@PublishedApi -internal fun buildName(expression: AsmNode<*>, 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) -} - -@PublishedApi -internal inline fun AsmNode.compile(algebra: Algebra): Expression = - AsmGenerator(T::class.java, algebra, buildName(this), this).getInstance() - -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - block: E.() -> AsmNode -): 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.() -> 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.() -> 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.() -> 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 = - asmRing { evaluate(ast) } 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 72a53d899..55d263117 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerator +import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -9,11 +9,11 @@ import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* /** - * A function declaration that could be compiled to [AsmGenerator]. + * A function declaration that could be compiled to [AsmBuilder]. * * @param T the type the stored function returns. */ -abstract class AsmNode internal constructor() { +abstract class AsmExpression internal constructor() { /** * Tries to evaluate this function without its variables. This method is intended for optimization. * @@ -24,18 +24,18 @@ abstract class AsmNode internal constructor() { /** * Compiles this declaration. * - * @param gen the target [AsmGenerator]. + * @param gen the target [AsmBuilder]. */ @PublishedApi - internal abstract fun compile(gen: AsmGenerator) + internal abstract fun compile(gen: AsmBuilder) } -internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : - AsmNode() { - private val expr: AsmNode = expr.optimize() +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 compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() if (!hasSpecific(context, name, 1)) @@ -47,11 +47,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val return gen.invokeAlgebraOperation( - owner = AsmGenerator.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "unaryOperation", - descriptor = "(L${AsmGenerator.STRING_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } @@ -59,11 +59,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val internal class AsmBinaryOperation( private val context: Algebra, private val name: String, - first: AsmNode, - second: AsmNode -) : AsmNode() { - private val first: AsmNode = first.optimize() - private val second: AsmNode = second.optimize() + first: AsmExpression, + second: AsmExpression +) : AsmExpression() { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() override fun tryEvaluate(): T? = context { binaryOperation( @@ -73,7 +73,7 @@ internal class AsmBinaryOperation( ) } - override fun compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() if (!hasSpecific(context, name, 2)) @@ -86,56 +86,56 @@ internal class AsmBinaryOperation( return gen.invokeAlgebraOperation( - owner = AsmGenerator.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "binaryOperation", - descriptor = "(L${AsmGenerator.STRING_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : - AsmNode() { - override fun compile(gen: AsmGenerator): Unit = gen.loadVariable(name, default) + AsmExpression() { + override fun compile(gen: AsmBuilder): Unit = gen.loadVariable(name, default) } internal class AsmConstantExpression(private val value: T) : - AsmNode() { + AsmExpression() { override fun tryEvaluate(): T = value - override fun compile(gen: AsmGenerator): Unit = gen.loadTConstant(value) + override fun compile(gen: AsmBuilder): Unit = gen.loadTConstant(value) } internal class AsmConstProductExpression( private val context: Space, - expr: AsmNode, + expr: AsmExpression, private val const: Number -) : AsmNode() { - private val expr: AsmNode = expr.optimize() +) : AsmExpression() { + private val expr: AsmExpression = expr.optimize() override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() gen.loadNumberConstant(const) expr.compile(gen) gen.invokeAlgebraOperation( - owner = AsmGenerator.SPACE_OPERATIONS_CLASS, + owner = AsmBuilder.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerator.OBJECT_CLASS};" + - "L${AsmGenerator.NUMBER_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.NUMBER_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : - AsmNode() { + AsmExpression() { override fun tryEvaluate(): T? = context.number(value) - override fun compile(gen: AsmGenerator): Unit = gen.loadNumberConstant(value) + override fun compile(gen: AsmBuilder): Unit = gen.loadNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( @@ -146,10 +146,10 @@ internal abstract class FunctionalCompiledExpression internal constructor( } /** - * A context class for [AsmNode] construction. + * A context class for [AsmExpression] construction. */ -interface AsmExpressionAlgebra> : NumericAlgebra>, - ExpressionAlgebra> { +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { /** * The algebra to provide for AsmExpressions built. */ @@ -158,108 +158,108 @@ interface AsmExpressionAlgebra> : NumericAlgebra = AsmNumberExpression(algebra, value) + 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): AsmNode = AsmConstantExpression(value) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) /** * Builds an AsmExpression to access a variable. */ - override fun variable(name: String, default: T?): AsmNode = AsmVariableExpression(name, default) + 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: AsmNode, right: AsmNode): AsmNode = + 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: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) } /** - * A context class for [AsmNode] construction for [Space] algebras. + * 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: AsmNode + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmExpression get() = const(algebra.zero) /** * Builds an AsmExpression of addition of two another expressions. */ - override fun add(a: AsmNode, b: AsmNode): AsmNode = + 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: AsmNode, k: Number): AsmNode = AsmConstProductExpression(algebra, a, k) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) - 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 + 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: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) } /** - * A context class for [AsmNode] construction for [Ring] algebras. + * 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: AsmNode + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmExpression get() = const(algebra.one) /** * Builds an AsmExpression of multiplication of two expressions. */ - override fun multiply(a: AsmNode, b: AsmNode): AsmNode = + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmNode.times(arg: T): AsmNode = this * const(arg) - operator fun T.times(arg: AsmNode): AsmNode = arg * this + 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: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } /** - * A context class for [AsmNode] construction for [Field] algebras. + * A context class for [AsmExpression] 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: AsmNode, b: AsmNode): AsmNode = + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmNode.div(arg: T): AsmNode = this / const(arg) - operator fun T.div(arg: AsmNode): AsmNode = arg / this + 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: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index cc3e36e94..a2bbb254c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,13 +1,12 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.* +@PublishedApi internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" @@ -20,28 +19,35 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String return buildName(expression, collision + 1) } -inline fun , E : AsmExpressionAlgebra> A.asm( +@PublishedApi +internal inline fun AsmExpression.compile(algebra: Algebra): Expression = + AsmBuilder(T::class.java, algebra, buildName(this), this).getInstance() + +inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, block: E.() -> AsmExpression -): Expression = expressionAlgebra.block() +): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) -inline fun NumericAlgebra.asm(ast: MST): Expression = - AsmExpressionAlgebra(T::class, this).evaluate(ast) +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(T::class, this).block() +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 = +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(T::class, this).block() +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 = +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(T::class, this).block() +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 = +inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = asmRing { evaluate(ast) } + diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt similarity index 96% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 0c6975cd9..720d44b45 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -4,13 +4,13 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.AsmExpression import scientifik.kmath.asm.FunctionalCompiledExpression -import scientifik.kmath.asm.internal.AsmGenerator.ClassLoader +import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.operations.Algebra /** - * ASM Generator is a structure that abstracts building a class that unwraps [AsmNode] to plain Java expression. + * ASM Builder 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. @@ -18,11 +18,11 @@ import scientifik.kmath.operations.Algebra * @param className the unique class name of new loaded class. */ @PublishedApi -internal class AsmGenerator @PublishedApi internal constructor( +internal class AsmBuilder @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val root: AsmNode + private val root: AsmExpression ) { 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) @@ -112,7 +112,7 @@ internal class AsmGenerator @PublishedApi internal constructor( visitCode() val l0 = Label() visitLabel(l0) - root.compile(this@AsmGenerator) + root.compile(this@AsmBuilder) visitReturnObject() val l1 = Label() visitLabel(l1) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt 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 3b37f81e5..2fb23d137 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,7 +2,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.AsmExpression import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -16,7 +16,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } -internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name context::class.java.methods.find { it.name == aName && it.parameters.size == arity } @@ -26,9 +26,9 @@ internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: St val sig = buildString { append('(') - repeat(arity) { append("L${AsmGenerator.OBJECT_CLASS};") } + repeat(arity) { append("L${AsmBuilder.OBJECT_CLASS};") } append(')') - append("L${AsmGenerator.OBJECT_CLASS};") + append("L${AsmBuilder.OBJECT_CLASS};") } invokeAlgebraOperation( @@ -43,7 +43,7 @@ internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: St } @PublishedApi -internal fun AsmNode.optimize(): AsmNode { +internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) } From 2580ab347e0fc53c8c030e63c3eb8de96e52b839 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 15 Jun 2020 17:37:11 +0700 Subject: [PATCH 6/9] Make ClassWriter extensions internal --- .../kotlin/scientifik/kmath/asm/internal/classWriters.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index 84f736ee7..95d713b18 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -3,9 +3,9 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.MethodVisitor -inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) -inline fun ClassWriter.visitMethod( +internal inline fun ClassWriter.visitMethod( access: Int, name: String, descriptor: String, From 15d7a20b43166b576df63a434bdcd0bc40632840 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:16:36 +0700 Subject: [PATCH 7/9] Add removed AsmCompiledExpression, move buildName to buildName.kt, refactor compileWith --- .../kotlin/scientifik/kmath/asm/asm.kt | 37 ++++++------------- .../kmath/asm/internal/AsmBuilder.kt | 17 ++++----- .../asm/internal/AsmCompiledExpression.kt | 12 ++++++ .../kmath/asm/internal/buildName.kt | 15 ++++++++ .../kmath/asm/internal/optimization.kt | 7 +--- 5 files changed, 46 insertions(+), 42 deletions(-) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 48e368fc3..e7644998e 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,6 +1,7 @@ package scientifik.kmath.asm import scientifik.kmath.asm.internal.AsmBuilder +import scientifik.kmath.asm.internal.buildName import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST @@ -11,44 +12,30 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.* import kotlin.reflect.KClass - /** * Compile given MST to an Expression using AST compiler */ fun MST.compileWith(type: KClass, algebra: Algebra): Expression { - - fun buildName(mst: MST, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(mst, collision + 1) - } - - fun AsmBuilder.visit(node: MST): Unit { + fun AsmBuilder.visit(node: MST) { when (node) { - is MST.Symbolic -> visitLoadFromVariables(node.value) + is MST.Symbolic -> loadVariable(node.value) is MST.Numeric -> { val constant = if (algebra is NumericAlgebra) { algebra.number(node.value) } else { error("Number literals are not supported in $algebra") } - visitLoadFromConstants(constant) + loadTConstant(constant) } is MST.Unary -> { - visitLoadAlgebra() + loadAlgebra() - if (!hasSpecific(algebra, node.operation, 1)) visitStringConstant(node.operation) + if (!hasSpecific(algebra, node.operation, 1)) loadStringConstant(node.operation) visit(node.value) if (!tryInvokeSpecific(algebra, node.operation, 1)) { - visitAlgebraOperation( + invokeAlgebraOperation( owner = AsmBuilder.ALGEBRA_CLASS, method = "unaryOperation", descriptor = "(L${AsmBuilder.STRING_CLASS};" + @@ -58,17 +45,17 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< } } is MST.Binary -> { - visitLoadAlgebra() + loadAlgebra() if (!hasSpecific(algebra, node.operation, 2)) - visitStringConstant(node.operation) + loadStringConstant(node.operation) visit(node.left) visit(node.right) if (!tryInvokeSpecific(algebra, node.operation, 2)) { - visitAlgebraOperation( + invokeAlgebraOperation( owner = AsmBuilder.ALGEBRA_CLASS, method = "binaryOperation", descriptor = "(L${AsmBuilder.STRING_CLASS};" + @@ -81,9 +68,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< } } - val builder = AsmBuilder(type.java, algebra, buildName(this)) - builder.visit(this) - return builder.generate() + return AsmBuilder(type.java, algebra, buildName(this)) { visit(this@compileWith) }.getInstance() } inline fun Algebra.compile(mst: MST): Expression = mst.compileWith(T::class, this) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 720d44b45..73ef77cc5 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -4,9 +4,8 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmExpression -import scientifik.kmath.asm.FunctionalCompiledExpression import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader +import scientifik.kmath.ast.MST import scientifik.kmath.operations.Algebra /** @@ -22,7 +21,7 @@ internal class AsmBuilder @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val root: AsmExpression + private val evaluateMethodVisitor: AsmBuilder.() -> Unit ) { 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) @@ -42,10 +41,10 @@ internal class AsmBuilder @PublishedApi internal constructor( private val invokeArgumentsVar: Int = 1 private val constants: MutableList = mutableListOf() private lateinit var invokeMethodVisitor: MethodVisitor - private var generatedInstance: FunctionalCompiledExpression? = null + private var generatedInstance: AsmCompiledExpression? = null @Suppress("UNCHECKED_CAST") - fun getInstance(): FunctionalCompiledExpression { + fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -112,7 +111,7 @@ internal class AsmBuilder @PublishedApi internal constructor( visitCode() val l0 = Label() visitLabel(l0) - root.compile(this@AsmBuilder) + evaluateMethodVisitor() visitReturnObject() val l1 = Label() visitLabel(l1) @@ -178,7 +177,7 @@ internal class AsmBuilder @PublishedApi internal constructor( .defineClass(className, classWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as AsmCompiledExpression generatedInstance = new return new @@ -296,13 +295,11 @@ internal class AsmBuilder @PublishedApi internal constructor( private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/asm/FunctionalCompiledExpression" + "scientifik/kmath/asm/internal/AsmCompiledExpression" 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 NUMBER_CLASS = "java/lang/Number" } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt new file mode 100644 index 000000000..0e113099a --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt @@ -0,0 +1,12 @@ +package scientifik.kmath.asm.internal + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra + +internal abstract class AsmCompiledExpression internal constructor( + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: Array +) : Expression { + abstract override fun invoke(arguments: Map): T +} + diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt new file mode 100644 index 000000000..65652d72b --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -0,0 +1,15 @@ +package scientifik.kmath.asm.internal + +import scientifik.kmath.ast.MST + +internal fun buildName(mst: MST, collision: Int = 0): String { + val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(mst, collision + 1) +} 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 029939f16..a39dba6b1 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 @@ -29,7 +29,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri append("L${AsmBuilder.OBJECT_CLASS};") } - visitAlgebraOperation( + invokeAlgebraOperation( owner = owner, method = aName, descriptor = sig, @@ -39,8 +39,3 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri return true } -// -//internal fun AsmExpression.optimize(): AsmExpression { -// val a = tryEvaluate() -// return if (a == null) this else AsmConstantExpression(type, algebra, a) -//} From 91a9e2a5e9c66821c0e5b008710b6cb798dbf9bd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:20:17 +0700 Subject: [PATCH 8/9] Remove @PublishedApi --- .../jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 73ef77cc5..5525e623f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -16,8 +16,7 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -@PublishedApi -internal class AsmBuilder @PublishedApi internal constructor( +internal class AsmBuilder internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, From c3cecc5a1611d4793dfaf4dd059d96a9f8455971 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:21:13 +0700 Subject: [PATCH 9/9] Rename variable --- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5525e623f..337219029 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -5,7 +5,6 @@ import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader -import scientifik.kmath.ast.MST import scientifik.kmath.operations.Algebra /** @@ -20,7 +19,7 @@ internal class AsmBuilder internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val evaluateMethodVisitor: AsmBuilder.() -> Unit + private val invokeLabel0Visitor: AsmBuilder.() -> Unit ) { 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) @@ -110,7 +109,7 @@ internal class AsmBuilder internal constructor( visitCode() val l0 = Label() visitLabel(l0) - evaluateMethodVisitor() + invokeLabel0Visitor() visitReturnObject() val l1 = Label() visitLabel(l1)