From e81a7f10c13f696aed8e21f36730c0a7f6445aa7 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Mon, 27 Jan 2020 16:01:45 +0300 Subject: [PATCH] [KLIB] Implement special encoding for Klib data --- .../encodings/BinaryCoordinates.kt | 22 ++ .../serialization/encodings/BinaryFlags.kt | 229 ++++++++++++++++++ .../serialization/encodings/BinaryLattice.kt | 42 ++++ .../encodings/BinaryNameAndType.kt | 16 ++ .../encodings/BinarySymbolData.kt | 41 ++++ .../encodings/BinaryTypeProjection.kt | 34 +++ 6 files changed, 384 insertions(+) create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryCoordinates.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryLattice.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryNameAndType.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinarySymbolData.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryTypeProjection.kt diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryCoordinates.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryCoordinates.kt new file mode 100644 index 00000000000..d14e315e504 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryCoordinates.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +inline class BinaryCoordinates(private val decoded: BinaryLattice) { + private fun diff(): Int = decoded.second + + val startOffset: Int get() = decoded.first + val endOffset: Int get() = startOffset + diff() + + companion object { + fun encode(startOffset: Int, endOffset: Int): Long { + assert(startOffset <= endOffset) + return BinaryLattice.encode(startOffset, endOffset - startOffset) + } + + fun decode(code: Long) = BinaryCoordinates(BinaryLattice.decode(code)).also { assert(it.startOffset <= it.endOffset) } + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt new file mode 100644 index 00000000000..39b2b670319 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt @@ -0,0 +1,229 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags +import org.jetbrains.kotlin.types.Variance + +inline class ClassFlags(val flags: Long) { + + val modality: Modality get() = ProtoEnumFlags.modality(Flags.MODALITY.get(flags.toInt())) + val visibility: Visibility get() = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags.toInt())) + val kind: ClassKind get() = ProtoEnumFlags.classKind(Flags.CLASS_KIND.get(flags.toInt())) + + val isCompanion: Boolean get() = Flags.CLASS_KIND.get(flags.toInt()) == ProtoBuf.Class.Kind.COMPANION_OBJECT + val isInner: Boolean get() = Flags.IS_INNER.get(flags.toInt()) + val isData: Boolean get() = Flags.IS_DATA.get(flags.toInt()) + val isInline: Boolean get() = Flags.IS_INLINE_CLASS.get(flags.toInt()) + val isExpect: Boolean get() = Flags.IS_EXPECT_CLASS.get(flags.toInt()) + val isExternal: Boolean get() = Flags.IS_EXTERNAL_CLASS.get(flags.toInt()) + val isFun: Boolean get() = Flags.IS_FUN_INTERFACE.get(flags.toInt()) + + companion object { + fun encode(clazz: IrClass): Long { + return clazz.run { + val hasAnnotation = annotations.isNotEmpty() + val visibility = ProtoEnumFlags.visibility(visibility) + val modality = ProtoEnumFlags.modality(modality) + val kind = ProtoEnumFlags.classKind(kind, isCompanion) + + val flags = + Flags.getClassFlags(hasAnnotation, visibility, modality, kind, isInner, isData, isExternal, isExpect, isInline, isFun) + + flags.toLong() + } + } + + fun decode(code: Long) = ClassFlags(code) + } +} + +inline class FunctionFlags(val flags: Long) { + + val modality: Modality get() = ProtoEnumFlags.modality(Flags.MODALITY.get(flags.toInt())) + val visibility: Visibility get() = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags.toInt())) + + val isOperator: Boolean get() = Flags.IS_OPERATOR.get(flags.toInt()) + val isInline: Boolean get() = Flags.IS_INLINE.get(flags.toInt()) + val isTailrec: Boolean get() = Flags.IS_TAILREC.get(flags.toInt()) + val isExternal: Boolean get() = Flags.IS_EXTERNAL_FUNCTION.get(flags.toInt()) + val isSuspend: Boolean get() = Flags.IS_SUSPEND.get(flags.toInt()) + val isExpect: Boolean get() = Flags.IS_EXPECT_FUNCTION.get(flags.toInt()) + val isFakeOverride: Boolean get() = kind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE + + val isPrimary: Boolean get() = Flags.IS_PRIMARY.get(flags.toInt()) + + private fun kind(): CallableMemberDescriptor.Kind = ProtoEnumFlags.memberKind(Flags.MEMBER_KIND.get(flags.toInt())) + + companion object { + fun encode(function: IrSimpleFunction): Long { + function.run { + val hasAnnotation = annotations.isNotEmpty() + val visibility = ProtoEnumFlags.visibility(visibility) + val modality = ProtoEnumFlags.modality(modality) + val kind = if (isFakeOverride) ProtoBuf.MemberKind.FAKE_OVERRIDE else ProtoBuf.MemberKind.DECLARATION + + + val flags = Flags.getFunctionFlags( + hasAnnotation, visibility, modality, kind, + isOperator, false, isInline, isTailrec, isExternal, isSuspend, isExpect + ) + + return flags.toLong() + } + } + + fun encode(constructor: IrConstructor): Long { + constructor.run { + val hasAnnotation = annotations.isNotEmpty() + val visibility = ProtoEnumFlags.visibility(visibility) + val flags = Flags.getConstructorFlags(hasAnnotation, visibility, isInline, isExternal, isExpect, isPrimary) + + return flags.toLong() + } + } + + fun decode(code: Long) = FunctionFlags(code) + } +} + +inline class PropertyFlags(val flags: Long) { + + val modality: Modality get() = ProtoEnumFlags.modality(Flags.MODALITY.get(flags.toInt())) + val visibility: Visibility get() = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags.toInt())) + + val isVar: Boolean get() = Flags.IS_VAR.get(flags.toInt()) + val isConst: Boolean get() = Flags.IS_CONST.get(flags.toInt()) + val isLateinit: Boolean get() = Flags.IS_LATEINIT.get(flags.toInt()) + val isExternal: Boolean get() = Flags.IS_EXTERNAL_PROPERTY.get(flags.toInt()) + val isDelegated: Boolean get() = Flags.IS_DELEGATED.get(flags.toInt()) + val isExpect: Boolean get() = Flags.IS_EXPECT_PROPERTY.get(flags.toInt()) + val isFakeOverride: Boolean get() = kind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE + + private fun kind(): CallableMemberDescriptor.Kind = ProtoEnumFlags.memberKind(Flags.MEMBER_KIND.get(flags.toInt())) + + companion object { + fun encode(property: IrProperty): Long { + return property.run { + val hasAnnotation = annotations.isNotEmpty() + val visibility = ProtoEnumFlags.visibility(visibility) + val modality = ProtoEnumFlags.modality(modality) + val kind = if (isFakeOverride) ProtoBuf.MemberKind.FAKE_OVERRIDE else ProtoBuf.MemberKind.DECLARATION + val hasGetter = getter != null + val hasSetter = setter != null + + val flags = Flags.getPropertyFlags( + hasAnnotation, visibility, modality, kind, + isVar, hasGetter, hasSetter, false, isConst, isLateinit, isExternal, isDelegated, isExpect + ) + + flags.toLong() + } + } + + fun decode(code: Long) = PropertyFlags(code) + } +} + +inline class ValueParameterFlags(val flags: Long) { + + val isCrossInline: Boolean get() = Flags.IS_CROSSINLINE.get(flags.toInt()) + val isNoInline: Boolean get() = Flags.IS_NOINLINE.get(flags.toInt()) + + companion object { + fun encode(param: IrValueParameter): Long { + return param.run { + Flags.getValueParameterFlags(annotations.isNotEmpty(), defaultValue != null, isCrossinline, isNoinline).toLong() + } + } + + fun decode(code: Long) = ValueParameterFlags(code) + } +} + +inline class TypeAliasFlags(val flags: Long) { + + val visibility: Visibility get() = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags.toInt())) + val isActual: Boolean get() = Flags.IS_ACTUAL.get(flags.toInt()) + + companion object { + fun encode(typeAlias: IrTypeAlias): Long { + return typeAlias.run { + val visibility = ProtoEnumFlags.visibility(visibility) + Flags.getTypeAliasFlags(annotations.isNotEmpty(), visibility, isActual).toLong() + } + } + + fun decode(code: Long) = TypeAliasFlags(code) + } +} + +inline class TypeParameterFlags(val flags: Long) { + + val variance: Variance get() = ProtoEnumFlags.variance(Flags.VARIANCE.get(flags.toInt())) + val isReified: Boolean get() = Flags.IS_REIFIED.get(flags.toInt()) + + companion object { + fun encode(typeParameter: IrTypeParameter): Long { + return typeParameter.run { + val variance = ProtoEnumFlags.variance(variance) + Flags.getTypeParameterFlags(annotations.isNotEmpty(), variance, isReified).toLong() + } + } + + fun decode(code: Long) = TypeParameterFlags(code) + } +} + +inline class FieldFlags(val flags: Long) { + + val visibility: Visibility get() = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags.toInt())) + val isFinal: Boolean get() = Flags.IS_FINAL.get(flags.toInt()) + val isExternal: Boolean get() = Flags.IS_EXTERNAL_FIELD.get(flags.toInt()) + val isStatic: Boolean get() = Flags.IS_STATIC.get(flags.toInt()) + val isFakeOverride: Boolean get() = Flags.IS_FAKE_OVERRIDE.get(flags.toInt()) + + companion object { + fun encode(field: IrField): Long { + return field.run { + val visibility = ProtoEnumFlags.visibility(visibility) + Flags.getFieldFlags(annotations.isNotEmpty(), visibility, isFinal, isExternal, isStatic, isFakeOverride).toLong() + } + } + + fun decode(code: Long) = FieldFlags(code) + } +} + +inline class LocalVariableFlags(val flags: Long) { + + val isVar: Boolean get() = Flags.IS_LOCAL_VAR.get(flags.toInt()) + val isConst: Boolean get() = Flags.IS_LOCAL_CONST.get(flags.toInt()) + val isLateinit: Boolean get() = Flags.IS_LOCAL_LATEINIT.get(flags.toInt()) + + companion object { + fun encode(variable: IrVariable): Long { + return variable.run { + Flags.getLocalFlags(annotations.isNotEmpty(), isVar, isConst, isLateinit).toLong() + } + } + + fun encode(delegate: IrLocalDelegatedProperty): Long { + return delegate.run { + Flags.getLocalFlags(annotations.isNotEmpty(), isVar, false, false).toLong() + } + } + + fun decode(code: Long) = LocalVariableFlags(code) + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryLattice.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryLattice.kt new file mode 100644 index 00000000000..b850d28650c --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryLattice.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +inline class BinaryLattice(private val code: Long) { + + val first: Int get() = decodeInt(code) + val second: Int get() = decodeInt(code ushr 1) + + companion object { + + private fun interleaveBits(input: Int): Long { + var word = input.toLong() and 0x0FFFFFFFFL + word = word xor (word shl 16) and 0x0000ffff0000ffffL + word = word xor (word shl 8) and 0x00ff00ff00ff00ffL + word = word xor (word shl 4) and 0x0f0f0f0f0f0f0f0fL + word = word xor (word shl 2) and 0x3333333333333333L + word = word xor (word shl 1) and 0x5555555555555555L + return word + } + + private fun decodeInt(xx: Long): Int { + var x = xx + x = x and 0x5555555555555555L + x = x xor (x shr 1) and 0x3333333333333333L + x = x xor (x shr 2) and 0x0f0f0f0f0f0f0f0fL + x = x xor (x shr 4) and 0x00ff00ff00ff00ffL + x = x xor (x shr 8) and 0x0000ffff0000ffffL + x = x xor (x shr 16) and 0x00000000FFFFFFFFL + return x.toInt() + } + + fun encode(f: Int, s: Int): Long { + return interleaveBits(f) or (interleaveBits(s) shl 1) + } + + fun decode(code: Long) = BinaryLattice(code) + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryNameAndType.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryNameAndType.kt new file mode 100644 index 00000000000..b385a34201e --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryNameAndType.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +inline class BinaryNameAndType(private val decoded: BinaryLattice) { + val nameIndex: Int get() = decoded.first + val typeIndex: Int get() = decoded.second + + companion object { + fun encode(nameIndex: Int, typeIndex: Int): Long = BinaryLattice.encode(nameIndex, typeIndex) + fun decode(code: Long) = BinaryNameAndType(BinaryLattice.decode(code)) + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinarySymbolData.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinarySymbolData.kt new file mode 100644 index 00000000000..9431c76e35f --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinarySymbolData.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +inline class BinarySymbolData(val code: Long) { + enum class SymbolKind { + FUNCTION_SYMBOL, + CONSTRUCTOR_SYMBOL, + ENUM_ENTRY_SYMBOL, + FIELD_SYMBOL, + VALUE_PARAMETER_SYMBOL, + RETURNABLE_BLOCK_SYMBOL, + CLASS_SYMBOL, + TYPE_PARAMETER_SYMBOL, + VARIABLE_SYMBOL, + ANONYMOUS_INIT_SYMBOL, + STANDALONE_FIELD_SYMBOL, // For fields without properties. WrappedFieldDescriptor, rather than WrappedPropertyDescriptor. + RECEIVER_PARAMETER_SYMBOL, // ReceiverParameterDescriptor rather than ValueParameterDescriptor. + PROPERTY_SYMBOL, + LOCAL_DELEGATED_PROPERTY_SYMBOL, + TYPEALIAS_SYMBOL; + } + + private fun symbolKindId(): Int = (code and 0xFF).toInt() + + val signatureId: Int get() = (code ushr 8).toInt() + val kind: SymbolKind + get() = SymbolKind.values()[symbolKindId()] + + companion object { + fun encode(kind: SymbolKind, signatureId: Int): Long { + val kindId = kind.ordinal + return (signatureId.toLong() shl 8) or kindId.toLong() + } + + fun decode(code: Long) = BinarySymbolData(code) + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryTypeProjection.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryTypeProjection.kt new file mode 100644 index 00000000000..743618a095e --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryTypeProjection.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.encodings + +import org.jetbrains.kotlin.types.Variance + +inline class BinaryTypeProjection(val code: Long) { + + private fun varianceId(): Int = (code and 0x3L).toInt() - 1 + + val isStarProjection: Boolean get() = code == 0L + + val variance: Variance + get() { + assert(!isStarProjection) + return Variance.values()[varianceId()] + } + + val typeIndex: Int get() = (code ushr 2).toInt() + + companion object { + fun encodeType(variance: Variance, typeIndex: Int): Long { + val vId = variance.ordinal + 1 + return (typeIndex.toLong() shl 2) or vId.toLong() + } + + fun decode(code: Long) = BinaryTypeProjection(code) + + const val STAR_CODE = 0L + } +} \ No newline at end of file