From a1f0188b8b0b3d5577c4ba8dec31ccf72ff2834e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 16 Jun 2020 16:29:52 +0300 Subject: [PATCH 1/7] Functional expression builders --- .../FunctionalExpressionAlgebra.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index c8d6e8eb0..a8a26aa33 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -36,12 +36,10 @@ internal class FunctionalConstProductExpression( /** * A context class for [Expression] construction. + * + * @param algebra The algebra to provide for Expressions built. */ -interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { - /** - * The algebra to provide for Expressions built. - */ - val algebra: A +abstract class FunctionalExpressionAlgebra>(val algebra: A) : ExpressionAlgebra> { /** * Builds an Expression of constant expression which does not depend on arguments. @@ -69,8 +67,8 @@ interface FunctionalExpressionAlgebra> : ExpressionAlgebra>(override val algebra: A) : - FunctionalExpressionAlgebra, Space> { +open class FunctionalExpressionSpace>(algebra: A) : + FunctionalExpressionAlgebra(algebra), Space> { override val zero: Expression get() = const(algebra.zero) @@ -98,7 +96,7 @@ open class FunctionalExpressionSpace>(override val algebra: A) : super.binaryOperation(operation, left, right) } -open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), +open class FunctionalExpressionRing(algebra: A) : FunctionalExpressionSpace(algebra), Ring> where A : Ring, A : NumericAlgebra { override val one: Expression get() = const(algebra.one) @@ -119,7 +117,7 @@ open class FunctionalExpressionRing(override val algebra: A) : FunctionalE super.binaryOperation(operation, left, right) } -open class FunctionalExpressionField(override val algebra: A) : +open class FunctionalExpressionField(algebra: A) : FunctionalExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { /** @@ -137,3 +135,12 @@ open class FunctionalExpressionField(override val algebra: A) : override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = super.binaryOperation(operation, left, right) } + +inline fun > A.expressionInSpace(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).block() + +inline fun > A.expressionInRing(block: FunctionalExpressionRing.() -> Expression): Expression = + FunctionalExpressionRing(this).block() + +inline fun > A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).block() \ No newline at end of file From e9ff33c4f95973aa3ebd5d376e847d453f2429f9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 19 Jun 2020 23:56:35 +0700 Subject: [PATCH 2/7] Write KDoc comments for AsmBuilder, minimal refactor of it --- .../kmath/asm/internal/AsmBuilder.kt | 82 +++++++++++++++++-- .../kmath/asm/internal/optimization.kt | 3 +- 2 files changed, 75 insertions(+), 10 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 337219029..649d06277 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 @@ -11,9 +11,12 @@ import scientifik.kmath.operations.Algebra * 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. - * @param algebra the algebra the applied AsmExpressions use. - * @param className the unique class name of new loaded class. + * @param T the type generated [AsmCompiledExpression] operates. + * @property classOfT the [Class] of T. + * @property algebra the algebra the applied AsmExpressions use. + * @property className the unique class name of new loaded class. + * @property invokeLabel0Visitor the function applied to this object when the L0 label of invoke implementation is + * being written. */ internal class AsmBuilder internal constructor( private val classOfT: Class<*>, @@ -21,10 +24,16 @@ internal class AsmBuilder internal constructor( private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit ) { + /** + * Internal classloader of [AsmBuilder] with alias to define class from byte array. + */ 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) } + /** + * The instance of [ClassLoader] used by this builder. + */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) @@ -35,14 +44,39 @@ internal class AsmBuilder internal constructor( private val T_CLASS: String = classOfT.name.replace('.', '/') private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + + /** + * Index of `this` variable in invoke method of [AsmCompiledExpression] built subclass. + */ private val invokeThisVar: Int = 0 + + /** + * Index of `arguments` variable in invoke method of [AsmCompiledExpression] built subclass. + */ private val invokeArgumentsVar: Int = 1 + + /** + * List of constants to provide to [AsmCompiledExpression] subclass. + */ private val constants: MutableList = mutableListOf() + + /** + * Method visitor of `invoke` method of [AsmCompiledExpression] subclass. + */ private lateinit var invokeMethodVisitor: MethodVisitor + + /** + * The cache of [AsmCompiledExpression] subclass built by this builder. + */ private var generatedInstance: AsmCompiledExpression? = null + /** + * Subclasses, loads and instantiates the [AsmCompiledExpression] for given parameters. + * + * The built instance is cached. + */ @Suppress("UNCHECKED_CAST") - fun getInstance(): AsmCompiledExpression { + internal fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -181,6 +215,9 @@ internal class AsmBuilder internal constructor( return new } + /** + * Loads a constant from + */ internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { loadNumberConstant(value as Number) @@ -191,6 +228,9 @@ internal class AsmBuilder internal constructor( loadConstant(value as Any, T_CLASS) } + /** + * Loads an object constant [value] stored in [AsmCompiledExpression.constants] and casts it to [type]. + */ private fun loadConstant(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex @@ -205,7 +245,12 @@ internal class AsmBuilder internal constructor( private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) - internal fun loadNumberConstant(value: Number) { + /** + * Either loads a numeric constant [value] from [AsmCompiledExpression.constants] field or boxes a primitive + * constant from the constant pool (some numbers with special opcodes like [Opcodes.ICONST_0] aren't even loaded + * from it). + */ + private fun loadNumberConstant(value: Number) { val clazz = value.javaClass val c = clazz.name.replace('.', '/') val sigLetter = SIGNATURE_LETTERS[clazz] @@ -225,6 +270,9 @@ internal class AsmBuilder internal constructor( loadConstant(value, c) } + /** + * Loads a variable [name] from [AsmCompiledExpression.invoke] [Map] parameter. The [defaultValue] may be provided. + */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { visitLoadObjectVar(invokeArgumentsVar) @@ -253,6 +301,9 @@ internal class AsmBuilder internal constructor( invokeMethodVisitor.visitCheckCast(T_CLASS) } + /** + * Loads algebra from according field of [AsmCompiledExpression] and casts it to class of [algebra] provided. + */ internal fun loadAlgebra() { loadThis() @@ -265,20 +316,32 @@ internal class AsmBuilder internal constructor( invokeMethodVisitor.visitCheckCast(T_ALGEBRA_CLASS) } + /** + * Writes a method instruction of opcode with its [owner], [method] and its [descriptor]. The default opcode is + * [Opcodes.INVOKEINTERFACE], since most Algebra functions are declared in interface. [loadAlgebra] should be + * called before the arguments and this operation. + * + * The result is casted to [T] automatically. + */ internal fun invokeAlgebraOperation( owner: String, method: String, descriptor: String, - opcode: Int = Opcodes.INVOKEINTERFACE, - isInterface: Boolean = true + opcode: Int = Opcodes.INVOKEINTERFACE ) { - invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, opcode == Opcodes.INVOKEINTERFACE) invokeMethodVisitor.visitCheckCast(T_CLASS) } + /** + * Writes a LDC Instruction with string constant provided. + */ internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) internal companion object { + /** + * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. + */ private val SIGNATURE_LETTERS: Map, String> by lazy { mapOf( java.lang.Byte::class.java to "B", @@ -290,6 +353,9 @@ internal class AsmBuilder internal constructor( ) } + /** + * Provides boxed number types values of which can be stored in JVM bytecode constant pool. + */ private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = 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 a39dba6b1..889f3accb 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 @@ -33,8 +33,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri owner = owner, method = aName, descriptor = sig, - opcode = Opcodes.INVOKEVIRTUAL, - isInterface = false + opcode = Opcodes.INVOKEVIRTUAL ) return true From ba499da2dac0b1487ac850974e6f8c5f721818e8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 20 Jun 2020 00:05:00 +0700 Subject: [PATCH 3/7] More KDoc comments --- .../kmath/asm/internal/AsmCompiledExpression.kt | 7 +++++++ .../kotlin/scientifik/kmath/asm/internal/buildName.kt | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) 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 index 0e113099a..a5236927c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt @@ -3,6 +3,13 @@ package scientifik.kmath.asm.internal import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra +/** + * [Expression] partial implementation to have it subclassed by actual implementations. Provides unified storage for + * objects needed to implement the expression. + * + * @property algebra the algebra to delegate calls. + * @property constants the constants array to have persistent objects to reference in [invoke]. + */ internal abstract class AsmCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, @JvmField protected val constants: Array 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 index 65652d72b..66bd039c3 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -2,7 +2,13 @@ package scientifik.kmath.asm.internal import scientifik.kmath.ast.MST -internal fun buildName(mst: MST, collision: Int = 0): String { +/** + * Creates a class name for [AsmCompiledExpression] subclassed to implement [mst] provided. + * + * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there + * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. + */ +internal tailrec fun buildName(mst: MST, collision: Int = 0): String { val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" try { From 635d708de5377d9e655136403eebb50ae886932a Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 20 Jun 2020 00:08:53 +0700 Subject: [PATCH 4/7] Add missing KDoc comments --- .../src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt | 2 +- .../scientifik/kmath/asm/internal/optimization.kt | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 bb091732e..43c1a377d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -73,7 +73,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< /** * Compile an [MST] to ASM using given algebra */ -inline fun Algebra.expresion(mst: MST): Expression = mst.compileWith(T::class, this) +inline fun Algebra.expression(mst: MST): Expression = mst.compileWith(T::class, this) /** * Optimize performance of an [MSTExpression] using ASM codegen 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 889f3accb..e81a7fd1a 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 @@ -5,6 +5,11 @@ import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +/** + * Checks if the target [context] for code generation contains a method with needed [name] and [arity]. + * + * @return `true` if contains, else `false`. + */ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name @@ -14,6 +19,12 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } +/** + * Checks if the target [context] for code generation contains a method with needed [name] and [arity] and inserts + * [AsmBuilder.invokeAlgebraOperation] of this method. + * + * @return `true` if contains, else `false`. + */ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name From 62ebda33021e9fc7dbf41e5d23ea57b0db5d9442 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 21 Jun 2020 20:23:50 +0700 Subject: [PATCH 5/7] Update readme, accident documentation-related refactor --- kmath-ast/README.md | 63 ++++++++++++++++++- .../kotlin/scientifik/kmath/asm/asm.kt | 10 ++- .../kmath/asm/internal/AsmBuilder.kt | 4 +- .../kmath/asm/internal/optimization.kt | 12 +++- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index f9dbd4663..b5ca5886f 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,4 +1,61 @@ -# AST based expression representation and operations +# AST-based expression representation and operations (`kmath-ast`) -## Dynamic expression code generation -Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). \ No newline at end of file +This subproject implements the following features: + +- Expression Language and its parser. +- MST as expression language's syntax intermediate representation. +- Type-safe builder of MST. +- Evaluating expressions by traversing MST. + +## Dynamic expression code generation with OW2 ASM + +`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds +a special implementation of `Expression` with implemented `invoke` function. + +For example, the following builder: + +```kotlin + RealField.mstInField { symbol("x") + 2 }.compile() +``` + +… leads to generation of bytecode, which can be decompiled to the following Java class: + +```java +package scientifik.kmath.asm.generated; + +import java.util.Map; +import scientifik.kmath.asm.internal.AsmCompiledExpression; +import scientifik.kmath.operations.Algebra; +import scientifik.kmath.operations.RealField; + +// The class's name is build with MST's hash-code and collision fixing number. +public final class AsmCompiledExpression_45045_0 extends AsmCompiledExpression { + // Plain constructor + public AsmCompiledExpression_45045_0(Algebra algebra, Object[] constants) { + super(algebra, constants); + } + + // The actual dynamic code: + public final Double invoke(Map arguments) { + return (Double)((RealField)super.algebra).add((Double)arguments.get("x"), (Double)2.0D); + } +} +``` + +### Example Usage + +This API is an extension to MST and MSTExpression APIs. You may optimize both MST and MSTExpression: + +```kotlin +RealField.mstInField { symbol("x") + 2 }.compile() +RealField.expression("2+2".parseMath()) +``` + +### Known issues + +- Using numeric algebras causes boxing and calling bridge methods. +- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid +class loading overhead. +- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. + +Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). 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 43c1a377d..350bb95bb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -4,11 +4,11 @@ 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 -import scientifik.kmath.ast.MSTExpression +import scientifik.kmath.ast.* import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField import kotlin.reflect.KClass /** @@ -78,4 +78,8 @@ inline fun Algebra.expression(mst: MST): Expression = ms /** * Optimize performance of an [MSTExpression] using ASM codegen */ -inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) \ No newline at end of file +inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) + +fun main() { + RealField.mstInField { symbol("x") + 2 }.compile() +} 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 649d06277..53fc4c4c1 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 @@ -8,7 +8,7 @@ import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.operations.Algebra /** - * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. + * ASM Builder is a structure that abstracts building a class for unwrapping [MST] 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 generated [AsmCompiledExpression] operates. @@ -343,7 +343,7 @@ internal class AsmBuilder internal constructor( * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ private val SIGNATURE_LETTERS: Map, String> by lazy { - mapOf( + hashMapOf( java.lang.Byte::class.java to "B", java.lang.Short::class.java to "S", java.lang.Integer::class.java to "I", 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 e81a7fd1a..efad97763 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 @@ -3,7 +3,13 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra -private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +private val methodNameAdapters: Map by lazy { + hashMapOf( + "+" to "add", + "*" to "multiply", + "/" to "divide" + ) +} /** * Checks if the target [context] for code generation contains a method with needed [name] and [arity]. @@ -13,7 +19,7 @@ private val methodNameAdapters: Map = mapOf("+" to "add", "*" to internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.java.methods.find { it.name == aName && it.parameters.size == arity } + context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } ?: return false return true @@ -28,7 +34,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo 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 } + context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } ?: return false val owner = context::class.java.name.replace('.', '/') From e99f7ad360d2c0efb448c7dcdd2825dc1c9d663c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 22 Jun 2020 04:05:52 +0700 Subject: [PATCH 6/7] Optimize constant pooling --- kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt | 6 +++--- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 1 + .../scientifik/kmath/asm/internal/methodVisitors.kt | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) 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 43c1a377d..feb3745b0 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -19,11 +19,11 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< when (node) { is MST.Symbolic -> loadVariable(node.value) is MST.Numeric -> { - val constant = if (algebra is NumericAlgebra) { + val constant = if (algebra is NumericAlgebra) algebra.number(node.value) - } else { + else error("Number literals are not supported in $algebra") - } + loadTConstant(constant) } is MST.Unary -> { 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 649d06277..8b6892235 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 @@ -260,6 +260,7 @@ internal class AsmBuilder internal constructor( is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) + is Long -> invokeMethodVisitor.visitLdcOrLongConstant(value) else -> invokeMethodVisitor.visitLdcInsn(value) } 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 70fb3bd44..6ecce8833 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 @@ -11,6 +11,8 @@ internal fun MethodVisitor.visitLdcOrIntConstant(value: Int): Unit = when (value 3 -> visitInsn(ICONST_3) 4 -> visitInsn(ICONST_4) 5 -> visitInsn(ICONST_5) + in -128..127 -> visitIntInsn(BIPUSH, value) + in -32768..32767 -> visitIntInsn(SIPUSH, value) else -> visitLdcInsn(value) } @@ -20,6 +22,12 @@ internal fun MethodVisitor.visitLdcOrDoubleConstant(value: Double): Unit = when else -> visitLdcInsn(value) } +internal fun MethodVisitor.visitLdcOrLongConstant(value: Long): Unit = when (value) { + 0L -> visitInsn(LCONST_0) + 1L -> visitInsn(LCONST_1) + else -> visitLdcInsn(value) +} + internal fun MethodVisitor.visitLdcOrFloatConstant(value: Float): Unit = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) From 29c6d2596733d25c4babc80aa90856b877153f42 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 22 Jun 2020 15:15:46 +0700 Subject: [PATCH 7/7] Optimize constant pooling for Byte and Short --- .../jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 2 ++ 1 file changed, 2 insertions(+) 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 8b6892235..ff66e69b0 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 @@ -257,6 +257,8 @@ internal class AsmBuilder internal constructor( if (sigLetter != null) { when (value) { + is Byte -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) + is Short -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value)