backend: implement compile-time eval of varargs and listOf;
also perform some refactoring around.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
e28a9c71a8
commit
415f2f1d35
+7
@@ -76,6 +76,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
fun load(value: LLVMValueRef, varName: String): LLVMValueRef = LLVMBuildLoad(builder, value, varName)!!
|
||||
fun store(value: LLVMValueRef, ptr: LLVMValueRef): LLVMValueRef = LLVMBuildStore(builder, value, ptr)!!
|
||||
|
||||
fun isConst(value: LLVMValueRef): Boolean = (LLVMIsConstant(value) == 1)
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun invoke(llvmFunction: LLVMValueRef?, args: List<LLVMValueRef?>,
|
||||
@@ -124,6 +126,11 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
fun classType(descriptor: ClassDescriptor): LLVMTypeRef = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!!
|
||||
fun typeInfoValue(descriptor: ClassDescriptor): LLVMValueRef = descriptor.llvmTypeInfoPtr
|
||||
|
||||
/**
|
||||
* Pointer to type info for given type, or `null` if the type doesn't have corresponding type info.
|
||||
*/
|
||||
fun typeInfoValue(type: KotlinType): LLVMValueRef? = type.typeInfoPtr?.llvm
|
||||
|
||||
fun param(fn: FunctionDescriptor, i: Int): LLVMValueRef {
|
||||
assert (i >= 0 && i < countParams(fn))
|
||||
return LLVMGetParam(fn.llvmFunction, i)!!
|
||||
|
||||
+8
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
/**
|
||||
* Provides utility methods to the implementer.
|
||||
@@ -94,6 +96,12 @@ internal interface ContextUtils {
|
||||
val ClassDescriptor.typeInfoPtr: ConstPointer
|
||||
get() = constPointer(this.llvmTypeInfoPtr)
|
||||
|
||||
/**
|
||||
* Pointer to type info for this type, or `null` if the type doesn't have corresponding type info.
|
||||
*/
|
||||
val KotlinType.typeInfoPtr: ConstPointer?
|
||||
get() = TypeUtils.getClassDescriptor(this)?.typeInfoPtr
|
||||
|
||||
/**
|
||||
* Returns contents of this [GlobalHash].
|
||||
*
|
||||
|
||||
+67
-5
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -694,12 +695,30 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateVararg(tmpVariableName: String, value: IrVararg): LLVMValueRef? {
|
||||
val arrayCreationArgs = listOf(codegen.kTheArrayTypeInfo, kImmInt32One, Int32(value.elements.size).llvm)
|
||||
val arguments = value.elements.map {
|
||||
assert (it !is IrSpreadElement)
|
||||
evaluateExpression(codegen.newVar(), it)!!
|
||||
}
|
||||
|
||||
return genCreateArray(tmpVariableName, value.type, arguments)
|
||||
}
|
||||
|
||||
private fun genCreateArray(tmpVariableName: String,
|
||||
arrayType: KotlinType, elements: List<LLVMValueRef>): LLVMValueRef {
|
||||
|
||||
if (elements.all { codegen.isConst(it) }) {
|
||||
// Note: even if all elements are const, they aren't guaranteed to be statically initialized.
|
||||
// E.g. an element may be a pointer to lazy-initialized object (aka singleton).
|
||||
// However it is guaranteed that all elements are already initialized at this point.
|
||||
return codegen.staticData.createKotlinArray(arrayType, elements)
|
||||
}
|
||||
|
||||
val typeInfo = codegen.typeInfoValue(arrayType)!!
|
||||
|
||||
val arrayCreationArgs = listOf(typeInfo, kImmInt32One, Int32(elements.size).llvm)
|
||||
val array = currentCodeContext.genCall(context.allocArrayFunction, arrayCreationArgs, tmpVariableName)
|
||||
value.elements.forEachIndexed { i, it ->
|
||||
val elementValueRaw = evaluateExpression(codegen.newVar(), it)
|
||||
currentCodeContext.genCall(context.setArrayFunction, listOf(array, Int32(i).llvm, elementValueRaw!!), "")
|
||||
return@forEachIndexed
|
||||
elements.forEachIndexed { i, it ->
|
||||
currentCodeContext.genCall(context.setArrayFunction, listOf(array, Int32(i).llvm, it), "")
|
||||
}
|
||||
return array
|
||||
}
|
||||
@@ -1367,11 +1386,54 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
/**
|
||||
* 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 { codegen.isConst(it) }) {
|
||||
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.staticData.createArrayList(elementType, array, length).llvm
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun evaluateCall(tmpVariableName: String, value: IrMemberAccessExpression): LLVMValueRef {
|
||||
logger.log("evaluateCall : $tmpVariableName = ${ir2string(value)}")
|
||||
|
||||
val args = evaluateExplicitArgs(value)
|
||||
|
||||
compileTimeEvaluate(value, args)?.let { return it }
|
||||
|
||||
when {
|
||||
value is IrDelegatingConstructorCall ->
|
||||
return delegatingConstructorCall(tmpVariableName, value.descriptor, args)
|
||||
|
||||
+6
-2
@@ -57,6 +57,8 @@ internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue
|
||||
|
||||
constructor(type: LLVMTypeRef?, vararg elements: ConstValue) : this(type, elements.toList())
|
||||
|
||||
constructor(vararg elements: ConstValue) : this(structType(elements.map { it.llvmType }), *elements)
|
||||
|
||||
override val llvm = memScoped {
|
||||
val values = elements.map { it.llvm }.toTypedArray()
|
||||
val valuesNativeArrayPtr = allocArrayOf(*values)[0].ptr
|
||||
@@ -128,8 +130,10 @@ internal val ContextUtils.kNullArrayHeaderPtr: LLVMValueRef
|
||||
|
||||
internal fun pointerType(pointeeType: LLVMTypeRef) = LLVMPointerType(pointeeType, 0)!!
|
||||
|
||||
internal fun structType(vararg types: LLVMTypeRef): LLVMTypeRef = memScoped {
|
||||
LLVMStructType(allocArrayOf(*types)[0].ptr, types.size, 0)!!
|
||||
internal fun structType(vararg types: LLVMTypeRef): LLVMTypeRef = structType(types.toList())
|
||||
|
||||
internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
|
||||
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!!
|
||||
}
|
||||
|
||||
internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
|
||||
|
||||
+6
-1
@@ -15,11 +15,16 @@ internal class StaticData(override val context: Context): ContextUtils {
|
||||
companion object {
|
||||
fun create(staticData: StaticData, type: LLVMTypeRef, name: String): Global {
|
||||
val module = staticData.context.llvmModule
|
||||
if (LLVMGetNamedGlobal(module, name) != null) {
|
||||
if (name != "" && LLVMGetNamedGlobal(module, name) != null) {
|
||||
throw IllegalArgumentException("Global '$name' already exists")
|
||||
}
|
||||
|
||||
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
||||
|
||||
if (name == "") {
|
||||
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
|
||||
}
|
||||
|
||||
return Global(staticData, llvmGlobal)
|
||||
}
|
||||
}
|
||||
|
||||
+113
-28
@@ -2,8 +2,13 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeProjection
|
||||
import org.jetbrains.kotlin.types.replace
|
||||
|
||||
private fun StaticData.objHeader(containerOffsetNegative: Int, typeInfo: ConstPointer): Struct {
|
||||
assert (containerOffsetNegative >= 0)
|
||||
@@ -16,41 +21,121 @@ private fun StaticData.arrayHeader(containerOffsetNegative: Int, typeInfo: Const
|
||||
return Struct(runtime.arrayHeaderType, objHeader, Int32(length))
|
||||
}
|
||||
|
||||
private fun requiredPadding(size: Int, align: Int): Int {
|
||||
val rem = size % align
|
||||
return (align - rem) % align
|
||||
}
|
||||
|
||||
private fun StaticData.staticContainerHeader(): Struct {
|
||||
val CONTAINER_TAG_NOCOUNT = 1 // FIXME: copy-pasted from runtime
|
||||
return Struct(runtime.containerHeaderType, Int32(CONTAINER_TAG_NOCOUNT))
|
||||
val header = Struct(runtime.containerHeaderType, Int32(CONTAINER_TAG_NOCOUNT))
|
||||
|
||||
// ArrayHeader struct is represented as packed in LLVM;
|
||||
// so add explicit padding to container's header to make its objects properly aligned:
|
||||
val llvmSize = LLVMABISizeOfType(llvmTargetData, header.type).toInt()
|
||||
val paddingSize = requiredPadding(llvmSize, 8)
|
||||
val padding = ConstArray(int8Type, ByteArray(paddingSize).map { Int8(it) })
|
||||
|
||||
// TODO: handle such cases more generally by using C types alignments instead of LLVM ones.
|
||||
|
||||
return Struct(header, padding)
|
||||
}
|
||||
|
||||
internal fun StaticData.createKotlinStringLiteral(value: IrConst<String>): ConstPointer {
|
||||
val base64Str = value.value.globalHashBase64
|
||||
val valueBytes = value.value.toByteArray(Charsets.UTF_8)
|
||||
val name = "kstr:" + value.value.globalHashBase64
|
||||
val elements = value.value.toByteArray(Charsets.UTF_8).map { Int8(it) }
|
||||
|
||||
val arrayCount = valueBytes.size
|
||||
val arrayType = LLVMArrayType(int8Type, arrayCount)
|
||||
val objRef = createKotlinArray(value.type, elements)
|
||||
|
||||
val compositeType = structType(runtime.containerHeaderType, runtime.arrayHeaderType, arrayType)
|
||||
|
||||
// TODO: use C types alignments instead of LLVM ones
|
||||
val global = this.createGlobal(compositeType, "kstrcont:$base64Str")
|
||||
LLVMSetLinkage(global.llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
|
||||
|
||||
val containerHeader = staticContainerHeader()
|
||||
|
||||
val objHeaderPtr = global.pointer.getElementPtr(1)
|
||||
val containerHeaderPtr = global.pointer.getElementPtr(0)
|
||||
val containerOffsetNegative = objHeaderPtr.sub(containerHeaderPtr)
|
||||
|
||||
val stringClass = value.type.constructor.declarationDescriptor as ClassDescriptor
|
||||
assert (stringClass.fqNameSafe.asString() == "kotlin.String")
|
||||
val arrayHeader = arrayHeader(containerOffsetNegative, stringClass.typeInfoPtr, arrayCount)
|
||||
|
||||
val array = ConstArray(int8Type, valueBytes.map { Int8(it) } )
|
||||
|
||||
global.setInitializer(Struct(compositeType, containerHeader, arrayHeader, array))
|
||||
|
||||
val stringRef = objHeaderPtr.bitcast(getLLVMType(value.type))
|
||||
val res = createAlias("kstr:$base64Str", stringRef)
|
||||
val res = createAlias(name, objRef)
|
||||
LLVMSetLinkage(res.llvm, LLVMLinkage.LLVMWeakAnyLinkage)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
private fun StaticData.createRef(type: KotlinType, objHeaderPtr: ConstPointer): ConstPointer {
|
||||
val llvmType = getLLVMType(type)
|
||||
return if (llvmType != objHeaderPtr.llvmType) {
|
||||
objHeaderPtr.bitcast(llvmType)
|
||||
} else {
|
||||
objHeaderPtr
|
||||
}
|
||||
}
|
||||
|
||||
internal fun StaticData.createKotlinArray(arrayType: KotlinType, elements: List<LLVMValueRef>) =
|
||||
createKotlinArray(arrayType, elements.map { constValue(it) }).llvm
|
||||
|
||||
internal fun StaticData.createKotlinArray(arrayType: KotlinType, elements: List<ConstValue>): ConstPointer {
|
||||
|
||||
val typeInfo = arrayType.typeInfoPtr!!
|
||||
|
||||
val bodyElementType: LLVMTypeRef = elements.firstOrNull()?.llvmType ?: int8Type
|
||||
// (use [0 x i8] as body if there are no elements)
|
||||
val arrayBody = ConstArray(bodyElementType, elements)
|
||||
|
||||
val containerHeader = staticContainerHeader()
|
||||
|
||||
val compositeType = structType(containerHeader.llvmType, runtime.arrayHeaderType, arrayBody.llvmType)
|
||||
|
||||
val global = this.createGlobal(compositeType, "")
|
||||
|
||||
val objHeaderPtr = global.pointer.getElementPtr(1)
|
||||
val containerHeaderPtr = global.pointer.getElementPtr(0)
|
||||
val containerOffsetNegative = objHeaderPtr.sub(containerHeaderPtr)
|
||||
val arrayHeader = arrayHeader(containerOffsetNegative, typeInfo, elements.size)
|
||||
|
||||
global.setInitializer(Struct(compositeType, containerHeader, arrayHeader, arrayBody))
|
||||
|
||||
return createRef(arrayType, objHeaderPtr)
|
||||
}
|
||||
|
||||
internal fun StaticData.createKotlinObject(type: KotlinType, body: ConstValue): ConstPointer {
|
||||
val typeInfo = type.typeInfoPtr!!
|
||||
|
||||
val containerHeader = staticContainerHeader()
|
||||
val compositeType = structType(containerHeader.llvmType, runtime.objHeaderType, body.llvmType)
|
||||
|
||||
val global = this.createGlobal(compositeType, "")
|
||||
|
||||
val objHeaderPtr = global.pointer.getElementPtr(1)
|
||||
val containerHeaderPtr = global.pointer.getElementPtr(0)
|
||||
val containerOffsetNegative = objHeaderPtr.sub(containerHeaderPtr)
|
||||
val objHeader = objHeader(containerOffsetNegative, typeInfo)
|
||||
|
||||
global.setInitializer(Struct(compositeType, containerHeader, objHeader, body))
|
||||
|
||||
return createRef(type, objHeaderPtr)
|
||||
}
|
||||
|
||||
private fun StaticData.getArrayListClass(): ClassDescriptor {
|
||||
val module = context.irModule.descriptor
|
||||
val pkg = module.getPackage(FqName.fromSegments(listOf("kotlin", "collections")))
|
||||
val classifier = pkg.memberScope.getContributedClassifier(Name.identifier("ArrayList"),
|
||||
NoLookupLocation.FROM_BACKEND)
|
||||
|
||||
return classifier as ClassDescriptor
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates static instance of `kotlin.collections.ArrayList<elementType>` with given values of fields.
|
||||
*
|
||||
* @param array value for `array: Array<E>` field.
|
||||
* @param length value for `length: Int` field.
|
||||
*/
|
||||
internal fun StaticData.createArrayList(elementType: TypeProjection, array: ConstPointer, length: Int): ConstPointer {
|
||||
val arrayListClass = getArrayListClass()
|
||||
|
||||
// type is ArrayList<elementType>:
|
||||
val type = arrayListClass.defaultType.replace(listOf(elementType))
|
||||
|
||||
// FIXME: properly import body type from stdlib
|
||||
|
||||
val body = Struct(
|
||||
array, // array: Array<E>
|
||||
Int32(0), // offset: Int
|
||||
Int32(length), // length: Int
|
||||
NullPointer(kObjHeader) // backing: ArrayList<E>?
|
||||
)
|
||||
|
||||
return createKotlinObject(type, body)
|
||||
}
|
||||
Reference in New Issue
Block a user