Extracted compile time evaluation to a separate IR lowering

This commit is contained in:
Igor Chevdar
2018-02-14 19:09:01 +03:00
parent 51e5410ffd
commit 6682d8083b
6 changed files with 80 additions and 50 deletions
@@ -134,6 +134,9 @@ internal class KonanLower(val context: Context) {
phaser.phase(KonanPhase.LOWER_VARARG) {
VarargInjectionLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_COMPILE_TIME_EVAL) {
CompileTimeEvaluateLowering(context).lower(irFile)
}
phaser.phase(KonanPhase.LOWER_COROUTINES) {
SuspendFunctionsLowering(context).runOnFilePostfix(irFile)
}
@@ -47,6 +47,7 @@ enum class KonanPhase(val description: String,
/* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", LOWER_SHARED_VARIABLES, LOWER_CALLABLES),
/* ... ... */ LOWER_INTEROP_PART2("Interop lowering, part 2", LOWER_LOCAL_FUNCTIONS),
/* ... ... */ LOWER_VARARG("Vararg lowering", LOWER_CALLABLES),
/* ... ... */ LOWER_COMPILE_TIME_EVAL("Compile time evaluation lowering", LOWER_VARARG),
/* ... ... */ LOWER_TAILREC("tailrec lowering", LOWER_LOCAL_FUNCTIONS),
/* ... ... */ LOWER_FINALLY("Finally blocks lowering", LOWER_INITIALIZERS, LOWER_LOCAL_FUNCTIONS, LOWER_TAILREC),
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", LOWER_TAILREC, LOWER_ENUMS),
@@ -206,6 +206,8 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
val kClassImpl = internalClass("KClassImpl")
val kClassImplConstructor by lazy { kClassImpl.constructors.single() }
val listOfInternal = internalFunction("listOfInternal")
private fun internalFunction(name: String): IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
@@ -1613,47 +1613,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
/**
* Tries to evaluate given expression with given (already evaluated) arguments in compile time.
* Returns `null` on failure.
*/
private fun compileTimeEvaluate(expression: IrMemberAccessExpression, args: List<LLVMValueRef>): LLVMValueRef? {
if (!args.all { it.isConst }) {
return null
}
val function = expression.descriptor
if (function.fqNameSafe.asString() == "kotlin.collections.listOf" && function.valueParameters.size == 1) {
val varargExpression = expression.getValueArgument(0) as? IrVararg
if (varargExpression != null) {
// The function is kotlin.collections.listOf<T>(vararg args: T).
// TODO: refer functions more reliably.
val vararg = args.single()
if (varargExpression.elements.any { it is IrSpreadElement }) {
return null // not supported yet, see `length` calculation below.
}
val length = varargExpression.elements.size
// TODO: store length in `vararg` itself when more abstract types will be used for values.
// `elementType` is type argument of function return type:
val elementType = function.returnType!!.arguments.single()
val array = constPointer(vararg)
// Note: dirty hack here: `vararg` has type `Array<out E>`, but `createArrayList` expects `Array<E>`;
// however `vararg` is immutable, and in current implementation it has type `Array<E>`,
// so let's ignore this mismatch currently for simplicity.
return context.llvm.staticData.createArrayList(elementType, array, length).llvm
}
}
return null
}
private fun evaluateSpecialIntrinsicCall(expression: IrFunctionAccessExpression): LLVMValueRef? {
if (expression.descriptor.isIntrinsic) {
when (expression.descriptor.original) {
@@ -1694,8 +1653,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val args = evaluateExplicitArgs(value)
compileTimeEvaluate(value, args)?.let { return it }
updateBuilderDebugLocation(value)
when {
value is IrDelegatingConstructorCall ->
@@ -2128,6 +2085,24 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.allocInstance(enumClassDescriptor, resultLifetime(callee))
}
context.ir.symbols.listOfInternal.descriptor -> {
val varargExpression = callee.getValueArgument(0) as IrVararg
val vararg = args.single()
val length = varargExpression.elements.size
// TODO: store length in `vararg` itself when more abstract types will be used for values.
// `elementType` is type argument of function return type:
val elementType = callee.descriptor.returnType!!.arguments.single()
val array = constPointer(vararg)
// Note: dirty hack here: `vararg` has type `Array<out E>`, but `createArrayList` expects `Array<E>`;
// however `vararg` is immutable, and in current implementation it has type `Array<E>`,
// so let's ignore this mismatch currently for simplicity.
return context.llvm.staticData.createArrayList(elementType, array, length).llvm
}
else -> TODO(callee.descriptor.original.toString())
}
}
@@ -0,0 +1,45 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
internal class CompileTimeEvaluateLowering(val context: Context): FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object: IrBuildingTransformer(context) {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val descriptor = expression.descriptor.original
// TODO
if (descriptor.fqNameSafe.asString() != "kotlin.collections.listOf" || descriptor.valueParameters.size != 1)
return expression
val elementsArr = expression.getValueArgument(0) as? IrVararg
?: return expression
// The function is kotlin.collections.listOf<T>(vararg args: T).
// TODO: refer functions more reliably.
if (elementsArr.elements.any { it is IrSpreadElement }
|| !elementsArr.elements.all { it is IrConst<*> && KotlinBuiltIns.isString(it.type) })
return expression
builder.at(expression)
val typeArguments = descriptor.typeParameters.map { expression.getTypeArgument(it)!! }
return builder.irCall(context.ir.symbols.listOfInternal, typeArguments).apply {
putValueArgument(0, elementsArr)
}
}
})
}
}
@@ -110,15 +110,10 @@ fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
}
@Intrinsic
internal fun <T> createUninitializedInstance(): T {
throw Exception("Call to this function should've been lowered")
}
internal external fun <T> createUninitializedInstance(): T
@Intrinsic
@Suppress("UNUSED_PARAMETER")
internal fun initInstance(thiz: Any, constructorCall: Any): Unit {
throw Exception("Call to this function should've been lowered")
}
internal external fun initInstance(thiz: Any, constructorCall: Any): Unit
fun checkProgressionStep(step: Int) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
fun checkProgressionStep(step: Long) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
@@ -146,3 +141,12 @@ fun KonanObjectToUtf8Array(value: Any?): ByteArray {
}
return string.toUtf8()
}
@Intrinsic
@PublishedApi
internal fun <T> listOfInternal(vararg elements: T): List<T> {
val result = ArrayList<T>(elements.size)
for (i in 0 until elements.size)
result.add(elements[i])
return result
}