Immutable data prototype. (#799)

This commit is contained in:
Nikolay Igotti
2017-08-22 16:21:10 +03:00
committed by GitHub
parent 87f2bed5c8
commit 109f84f9af
12 changed files with 215 additions and 15 deletions
@@ -58,6 +58,10 @@ class KonanBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageMana
val nativePtrPlusLong by lazy { nativePtr.unsubstitutedMemberScope.getContributedFunctions("plus").single() }
val nativePtrToLong by lazy { nativePtr.unsubstitutedMemberScope.getContributedFunctions("toLong").single() }
val getNativeNullPtr by lazy { packageScope.getContributedFunctions("getNativeNullPtr").single() }
val immutableBinaryBlobOf by lazy {
builtInsModule.getPackage(FqName("konan")).memberScope.
getContributedFunctions("immutableBinaryBlobOf").single()
}
private fun MemberScope.getContributedClassifier(name: String) =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
@@ -95,7 +95,8 @@ private val arrayTypes = setOf(
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray"
"kotlin.BooleanArray",
"konan.ImmutableBinaryBlob"
)
internal val ClassDescriptor.isIntrinsic: Boolean
@@ -83,6 +83,13 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
}
}.toMap()
val immutableBinaryBlob = symbolTable.referenceClass(
builtInsPackage("konan").getContributedClassifier(
Name.identifier("ImmutableBinaryBlob"), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
val immutableBinaryBlobOf = symbolTable.referenceSimpleFunction(context.builtIns.immutableBinaryBlobOf)
val scheduleImpl = symbolTable.referenceSimpleFunction(context.interopBuiltIns.scheduleImplFunction)
@@ -1383,6 +1383,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
//-------------------------------------------------------------------------//
private fun evaluateStringConst(value: IrConst<String>) =
if (this.produceImmutableBinaryBlob) {
context.llvm.staticData.createImmutableBinaryBlob(value)
} else {
context.llvm.staticData.kotlinStringLiteral(
context.builtIns.stringType, value).llvm
}
private fun evaluateConst(value: IrConst<*>): LLVMValueRef {
context.log{"evaluateConst : ${ir2string(value)}"}
@@ -1397,8 +1404,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
IrConstKind.Short -> return LLVMConstInt(LLVMInt16Type(), (value.value as Short).toLong(), 1)!!
IrConstKind.Int -> return LLVMConstInt(LLVMInt32Type(), (value.value as Int).toLong(), 1)!!
IrConstKind.Long -> return LLVMConstInt(LLVMInt64Type(), value.value as Long, 1)!!
IrConstKind.String -> return context.llvm.staticData.kotlinStringLiteral(
context.builtIns.stringType, value as IrConst<String>).llvm
IrConstKind.String -> return evaluateStringConst(value as IrConst<String>)
IrConstKind.Float -> return LLVMConstRealOfString(LLVMFloatType(), (value.value as Float).toString())!!
IrConstKind.Double -> return LLVMConstRealOfString(LLVMDoubleType(), (value.value as Double).toString())!!
}
@@ -1757,16 +1763,30 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun CallableDescriptor.returnsUnit() = returnType == context.builtIns.unitType && !isSuspend
private var produceImmutableBinaryBlob: Boolean = false
/**
* Evaluates all arguments of [expression] that are explicitly represented in the IR.
* Returns results in the same order as LLVM function expects, assuming that all explicit arguments
* exactly correspond to a tail of LLVM parameters.
*/
private fun evaluateExplicitArgs(expression: IrMemberAccessExpression): List<LLVMValueRef> {
// TODO: remove this hack by properly implementing blobs in the frontend.
if (expression.descriptor.original == context.builtIns.immutableBinaryBlobOf) {
// As calls to immutableBinaryBlobOf() cannot be composed, it's OK to
// have simple flag for that purpose.
assert(!produceImmutableBinaryBlob)
produceImmutableBinaryBlob = true
}
val evaluatedArgs = expression.getArguments().map { (param, argExpr) ->
param to evaluateExpression(argExpr)
}.toMap()
if (produceImmutableBinaryBlob)
produceImmutableBinaryBlob = false
val allValueParameters = expression.descriptor.allParameters
return allValueParameters.dropWhile { it !in evaluatedArgs }.map {
@@ -1968,6 +1988,12 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
LLVMBuildSExt(functionGenerationContext.builder, intPtrValue, resultType, "")!!
}
}
context.builtIns.immutableBinaryBlobOf -> {
// LLVM value is already computed when evaluating argument, just use it.
args.single()
}
interop.objCObjectInitFromPtr -> {
genObjCObjectInitFromPtr(args)
}
@@ -78,16 +78,17 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
private val arrayClasses = mapOf(
"kotlin.Array" to -LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt(),
"kotlin.ByteArray" to -1,
"kotlin.CharArray" to -2,
"kotlin.ShortArray" to -2,
"kotlin.IntArray" to -4,
"kotlin.LongArray" to -8,
"kotlin.FloatArray" to -4,
"kotlin.DoubleArray" to -8,
"kotlin.BooleanArray" to -1,
"kotlin.String" to -2
"kotlin.Array" to -LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt(),
"kotlin.ByteArray" to -1,
"kotlin.CharArray" to -2,
"kotlin.ShortArray" to -2,
"kotlin.IntArray" to -4,
"kotlin.LongArray" to -8,
"kotlin.FloatArray" to -4,
"kotlin.DoubleArray" to -8,
"kotlin.BooleanArray" to -1,
"kotlin.String" to -2,
"konan.ImmutableBinaryBlob" to -1
)
private fun getInstanceSize(classType: LLVMTypeRef?, className: FqName) : Int {
@@ -160,3 +160,13 @@ internal class StaticData(override val context: Context): ContextUtils {
fun kotlinStringLiteral(type: KotlinType, value: IrConst<String>) =
stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) }
}
/**
* Creates static instance of `konan.ImmutableByteArray` with given values of elements.
*
* @param args data for constant creation.
*/
internal fun StaticData.createImmutableBinaryBlob(value: IrConst<String>): LLVMValueRef {
val args = value.value.map { Int8(it.toByte()).llvm }
return createKotlinArray(context.ir.symbols.immutableBinaryBlob.descriptor.defaultType, args)
}
@@ -68,7 +68,6 @@ internal fun StaticData.createKotlinArray(arrayType: KotlinType, elements: List<
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
@@ -143,6 +142,7 @@ internal fun StaticData.createArrayList(elementType: TypeProjection, array: Cons
return createKotlinObject(type, body)
}
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor,
bodyType: LLVMTypeRef,
typeInfo: ConstPointer
@@ -1,12 +1,17 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrSpreadElement
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -14,6 +19,7 @@ import org.jetbrains.kotlin.types.typeUtil.isUnit
/**
* This pass runs before inlining and performs the following additional transformations over some calls:
* - Assertion call removal.
* - Convert immutableBinaryBlobOf() arguments to special IrConst.
*/
internal class SpecialCallsLowering(val context: Context) : FileLoweringPass {
@@ -32,6 +38,38 @@ internal class SpecialCallsLowering(val context: Context) : FileLoweringPass {
return IrCompositeImpl(expression.startOffset, expression.endOffset, expression.type)
}
if (expression.symbol == context.ir.symbols.immutableBinaryBlobOf) {
// Convert arguments of the binary blob to special IrConst<String> structure, so that
// vararg lowering will not affect it.
val args = expression.getValueArgument(0) as? IrVararg
if (args == null) throw Error("varargs shall not be lowered yet")
if (args.elements.any { it is IrSpreadElement }) {
context.reportCompilationError("no spread elements allowed here", irFile, args)
}
val builder = StringBuilder()
args.elements.forEach {
if (it !is IrConst<*>) {
context.reportCompilationError(
"all elements of binary blob must be constants", irFile, it)
}
val value = when (it.kind) {
IrConstKind.Short -> (it.value as Short).toInt()
else ->
context.reportCompilationError("incorrect value for binary data: $it.value", irFile, it)
}
if (value < 0 || value > 0xff)
context.reportCompilationError("incorrect value for binary data: $value", irFile, it)
// Luckily, all values in range 0x00 .. 0xff represent valid UTF-16 symbols,
// block 0 (Basic Latin) and block 1 (Latin-1 Supplement) in
// Basic Multilingual Plane, so we could just append data "as is".
builder.append(value.toChar())
}
expression.putValueArgument(0, IrConstImpl<String>(
expression.startOffset, expression.endOffset,
context.ir.symbols.immutableBinaryBlob.descriptor.defaultType,
IrConstKind.String, builder.toString()))
}
return expression
}
})
+5
View File
@@ -1081,6 +1081,11 @@ task array2(type: RunKonanTest) {
source = "runtime/collections/array2.kt"
}
task array3(type: RunKonanTest) {
goldValue = "1 2 3 7 8 9 -128 -1 \n1 2 3 7 8 9 -128 -1 \n"
source = "runtime/collections/array3.kt"
}
task sort0(type: RunKonanTest) {
goldValue = "[a, b, x]\n[-1, 0, 42, 239, 100500]\n"
source = "runtime/collections/sort0.kt"
@@ -0,0 +1,13 @@
import konan.*
fun main(args : Array<String>) {
val data = immutableBinaryBlobOf(0x1, 0x2, 0x3, 0x7, 0x8, 0x9, 0x80, 0xff)
for (b in data) {
print("$b ")
}
println()
val dataClone = data.toByteArray()
dataClone.map { print("$it ") }
println()
}
+21
View File
@@ -329,4 +329,25 @@ KInt Kotlin_BooleanArray_getArrayLength(KConstRef thiz) {
return array->count_;
}
OBJ_GETTER(Kotlin_ImmutableBinaryBlob_toByteArray, KConstRef thiz, KInt start, KInt count) {
const ArrayHeader* array = thiz->array();
if (start < 0 || count < 0 || start > array->count_ - count) {
ThrowArrayIndexOutOfBoundsException();
}
ArrayHeader* result = AllocArrayInstance(
theByteArrayTypeInfo, count, OBJ_RESULT)->array();
memcpy(PrimitiveArrayAddressOfElementAt<KByte>(result, 0),
PrimitiveArrayAddressOfElementAt<KByte>(array, start),
count);
RETURN_OBJ(result->obj());
}
KNativePtr Kotlin_ImmutableBinaryBlob_asCPointerImpl(KRef thiz, KInt offset) {
ArrayHeader* array = thiz->array();
if (offset < 0 || offset > array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return PrimitiveArrayAddressOfElementAt<KByte>(array, offset);
}
} // extern "C"
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package konan
import konan.internal.*
import kotlinx.cinterop.*
/**
* An immutable compile-time array of bytes.
*/
@ExportTypeInfo("theImmutableBinaryBlobTypeInfo")
public final class ImmutableBinaryBlob private constructor() {
public val size: Int
get() = getArrayLength()
// Data layout is the same as for ByteArray, so we can share native functions.
@SymbolName("Kotlin_ByteArray_get")
external public operator fun get(index: Int): Byte
@SymbolName("Kotlin_ByteArray_getArrayLength")
external private fun getArrayLength(): Int
/** Creates an iterator over the elements of the array. */
public operator fun iterator(): ByteIterator {
return ImmutableBinaryBlobIteratorImpl(this)
}
}
private class ImmutableBinaryBlobIteratorImpl(
val collection: ImmutableBinaryBlob) : ByteIterator() {
var index : Int = 0
public override fun nextByte(): Byte {
if (!hasNext()) throw NoSuchElementException("$index")
return collection[index++]
}
public override operator fun hasNext(): Boolean {
return index < collection.size
}
}
// Allocates new ByteArray and copies the data.
@SymbolName("Kotlin_ImmutableBinaryBlob_toByteArray")
public external fun ImmutableBinaryBlob.toByteArray(start: Int, count: Int): ByteArray
public fun ImmutableBinaryBlob.toByteArray() = toByteArray(0, size)
// Returns stable C pointer to data at certain offset, useful as a way to pass resource
// to C API.
public external fun ImmutableBinaryBlob.asCPointer(offset: Int) =
interpretCPointer<ByteVar>(asCPointerImpl(offset))
@SymbolName("Kotlin_ImmutableBinaryBlob_asCPointerImpl")
private external fun ImmutableBinaryBlob.asCPointerImpl(offset: Int): konan.internal.NativePtr
// Creates ImmutableBinaryBlob out of compile-time constant data.
// This method accepts Short type, so that values in range 0x80 .. 0xff can be
// provided without toByte() cast. One element still represent one byte in the output data.
// This is the only way to create ImmutableBinaryBlob for now.
// TODO: reconsider?
@Intrinsic
public external fun immutableBinaryBlobOf(vararg elements: Short): ImmutableBinaryBlob