Immutable data prototype. (#799)
This commit is contained in:
+4
@@ -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)
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+7
@@ -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)
|
||||
|
||||
|
||||
+28
-2
@@ -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)
|
||||
}
|
||||
|
||||
+11
-10
@@ -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 {
|
||||
|
||||
+10
@@ -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)
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
|
||||
+39
-1
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user