diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/BackendContext.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/BackendContext.kt deleted file mode 100644 index 3326ad404ce..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/BackendContext.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common - -import org.jetbrains.kotlin.backend.common.descriptors.SharedVariablesManager -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns - -interface BackendContext { - val builtIns: KotlinBuiltIns - val irBuiltIns: IrBuiltIns - val sharedVariablesManager: SharedVariablesManager -} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/Lower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/Lower.kt deleted file mode 100644 index b3a9f7e9c46..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/Lower.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common - -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.expressions.IrBody -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.acceptVoid - -interface FileLoweringPass { - fun lower(irFile: IrFile) -} - -interface ClassLoweringPass { - fun lower(irClass: IrClass) -} - -interface DeclarationContainerLoweringPass { - fun lower(irDeclarationContainer: IrDeclarationContainer) -} - -interface FunctionLoweringPass { - fun lower(irFunction: IrFunction) -} - -interface BodyLoweringPass { - fun lower(irBody: IrBody) -} - -fun ClassLoweringPass.runOnFilePostfix(irFile: IrFile) { - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitClass(declaration: IrClass) { - declaration.acceptChildrenVoid(this) - lower(declaration) - } - }) -} - -fun DeclarationContainerLoweringPass.asClassLoweringPass() = object : ClassLoweringPass { - override fun lower(irClass: IrClass) { - this@asClassLoweringPass.lower(irClass) - } -} - -fun DeclarationContainerLoweringPass.runOnFilePostfix(irFile: IrFile) { - this.asClassLoweringPass().runOnFilePostfix(irFile) - this.lower(irFile) -} - -fun BodyLoweringPass.runOnFilePostfix(irFile: IrFile) { - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitBody(body: IrBody) { - body.acceptChildrenVoid(this) - lower(body) - } - }) -} - -fun FunctionLoweringPass.runOnFilePostfix(irFile: IrFile) { - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFunction(declaration: IrFunction) { - declaration.acceptChildrenVoid(this) - lower(declaration) - } - }) -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/SharedVariablesManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/SharedVariablesManager.kt deleted file mode 100644 index 44269840835..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/SharedVariablesManager.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.descriptors - -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrVariable -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrGetValue -import org.jetbrains.kotlin.ir.expressions.IrSetVariable - -interface SharedVariablesManager { - fun createSharedVariableDescriptor(variableDescriptor: VariableDescriptor): VariableDescriptor - fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement - fun getSharedValue(sharedVariableDescriptor: VariableDescriptor, originalGet: IrGetValue): IrExpression - fun setSharedValue(sharedVariableDescriptor: VariableDescriptor, originalSet: IrSetVariable): IrExpression -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalFunctionsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalFunctionsLowering.kt deleted file mode 100644 index 3684503a166..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalFunctionsLowering.kt +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.lower - -import org.jetbrains.kotlin.backend.common.* -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.util.transformFlat -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.descriptorUtil.parents -import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf -import org.jetbrains.kotlin.types.KotlinType -import java.util.* - -class LocalFunctionsLowering(val context: BackendContext): DeclarationContainerLoweringPass { - override fun lower(irDeclarationContainer: IrDeclarationContainer) { - irDeclarationContainer.declarations.transformFlat { memberDeclaration -> - if (memberDeclaration is IrFunction) - LocalFunctionsTransformer(memberDeclaration).lowerLocalFunctions() - else - null - } - } - - private class LocalFunctionContext(val declaration: IrFunction) { - lateinit var closure: Closure - - val closureParametersCount: Int get() = closure.capturedValues.size - - lateinit var transformedDescriptor: FunctionDescriptor - - val old2new: MutableMap = HashMap() - - var index: Int = -1 - - override fun toString(): String = - "LocalFunctionContext for ${declaration.descriptor}" - } - - private inner class LocalFunctionsTransformer(val memberFunction: IrFunction) { - val localFunctions: MutableMap = LinkedHashMap() - val new2old: MutableMap = HashMap() - - fun lowerLocalFunctions(): List? { - collectLocalFunctions() - if (localFunctions.isEmpty()) return null - - collectClosures() - - transformDescriptors() - - rewriteBodies() - - return collectRewrittenDeclarations() - } - - private fun collectRewrittenDeclarations(): ArrayList = - ArrayList(localFunctions.size + 1).apply { - add(memberFunction) - - localFunctions.values.mapTo(this) { - val original = it.declaration - IrFunctionImpl( - original.startOffset, original.endOffset, original.origin, - it.transformedDescriptor, - original.body - ) - } - } - - private inner class FunctionBodiesRewriter(val localFunctionContext: LocalFunctionContext?) : IrElementTransformerVoid() { - - override fun visitClass(declaration: IrClass): IrStatement { - // ignore local classes for now - return declaration - } - - override fun visitFunction(declaration: IrFunction): IrStatement { - // replace local function definition with an empty composite - return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType) - } - - override fun visitGetValue(expression: IrGetValue): IrExpression { - val remapped = localFunctionContext?.let { it.old2new[expression.descriptor] } - return if (remapped == null) - expression - else - IrGetValueImpl(expression.startOffset, expression.endOffset, remapped, expression.origin) - } - - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid(this) - - val oldCallee = expression.descriptor.original - val localFunctionData = localFunctions[oldCallee] ?: return expression - - val newCallee = localFunctionData.transformedDescriptor - - val newCall = createNewCall(expression, newCallee).fillArguments(localFunctionData, expression) - - return newCall - } - - private fun T.fillArguments(calleeContext: LocalFunctionContext, oldExpression: IrMemberAccessExpression): T { - val closureParametersCount = calleeContext.closureParametersCount - - mapValueParametersIndexed { index, newValueParameterDescriptor -> - val capturedValueDescriptor = new2old[newValueParameterDescriptor] ?: - throw AssertionError("Non-mapped parameter $newValueParameterDescriptor") - if (index >= closureParametersCount) - oldExpression.getValueArgument(capturedValueDescriptor as ValueParameterDescriptor) - else { - val remappedValueDescriptor = localFunctionContext?.let { it.old2new[capturedValueDescriptor] } - IrGetValueImpl(oldExpression.startOffset, oldExpression.endOffset, - remappedValueDescriptor ?: capturedValueDescriptor) - } - - } - - dispatchReceiver = oldExpression.dispatchReceiver - extensionReceiver = oldExpression.extensionReceiver - - return this - } - - override fun visitCallableReference(expression: IrCallableReference): IrExpression { - expression.transformChildrenVoid(this) - - val oldCallee = expression.descriptor.original - val localFunctionData = localFunctions[oldCallee] ?: return expression - val newCallee = localFunctionData.transformedDescriptor - - val newCallableReference = IrCallableReferenceImpl( - expression.startOffset, expression.endOffset, - expression.type, // TODO functional type for transformed descriptor - newCallee, - remapTypeArguments(expression, newCallee), - expression.origin - ).fillArguments(localFunctionData, expression) - - return newCallableReference - } - - override fun visitReturn(expression: IrReturn): IrExpression { - expression.transformChildrenVoid(this) - - val oldReturnTarget = expression.returnTarget - val localFunctionData = localFunctions[oldReturnTarget] ?: return expression - val newReturnTarget = localFunctionData.transformedDescriptor - - return IrReturnImpl(expression.startOffset, expression.endOffset, newReturnTarget, expression.value) - } - } - - private fun rewriteFunctionDeclaration(irFunction: IrFunction, localFunctionContext: LocalFunctionContext?) { - irFunction.transformChildrenVoid(FunctionBodiesRewriter(localFunctionContext)) - } - - private fun rewriteBodies() { - localFunctions.values.forEach { - rewriteFunctionDeclaration(it.declaration, it) - } - - rewriteFunctionDeclaration(memberFunction, null) - } - - private fun createNewCall(oldCall: IrCall, newCallee: FunctionDescriptor) = - if (oldCall is IrCallWithShallowCopy) - oldCall.shallowCopy(oldCall.origin, newCallee, oldCall.superQualifier) - else - IrCallImpl( - oldCall.startOffset, oldCall.endOffset, - newCallee, - remapTypeArguments(oldCall, newCallee), - oldCall.origin, oldCall.superQualifier - ) - - private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: FunctionDescriptor): Map? { - val oldCallee = oldExpression.descriptor - - return if (oldCallee.typeParameters.isEmpty()) - null - else oldCallee.typeParameters.associateBy( - { newCallee.typeParameters[it.index] }, - { oldExpression.getTypeArgument(it)!! } - ) - } - - private fun transformDescriptors() { - localFunctions.values.forEach { - it.transformedDescriptor = createTransformedDescriptor(it) - } - } - - private fun suggestLocalName(descriptor: DeclarationDescriptor): String { - val localFunctionContext = localFunctions[descriptor] - return if (localFunctionContext != null && localFunctionContext.index >= 0) - "lambda-${localFunctionContext.index}" - else - descriptor.name.asString() - } - - private fun generateNameForLiftedFunction(functionDescriptor: FunctionDescriptor): Name = - Name.identifier( - functionDescriptor.parentsWithSelf - .takeWhile { it is FunctionDescriptor } - .toList().reversed() - .map { suggestLocalName(it) } - .joinToString(separator = "$") - ) - - private fun createTransformedDescriptor(localFunctionContext: LocalFunctionContext): FunctionDescriptor { - val oldDescriptor = localFunctionContext.declaration.descriptor - - val memberOwner = memberFunction.descriptor.containingDeclaration - val newDescriptor = SimpleFunctionDescriptorImpl.create( - memberOwner, - oldDescriptor.annotations, - generateNameForLiftedFunction(oldDescriptor), - CallableMemberDescriptor.Kind.SYNTHESIZED, - oldDescriptor.source - ) - - val closureParametersCount = localFunctionContext.closureParametersCount - val newValueParametersCount = closureParametersCount + oldDescriptor.valueParameters.size - - val newDispatchReceiverParameter = - if (memberOwner is ClassDescriptor && oldDescriptor.dispatchReceiverParameter != null) - memberOwner.thisAsReceiverParameter - else - null - - // Do not substitute type parameters for now. - val newTypeParameters = oldDescriptor.typeParameters - - val newValueParameters = ArrayList(newValueParametersCount).apply { - localFunctionContext.closure.capturedValues.mapIndexedTo(this) { i, capturedValueDescriptor -> - createUnsubstitutedCapturedValueParameter(newDescriptor, capturedValueDescriptor, i).apply { - localFunctionContext.recordRemapped(capturedValueDescriptor, this) - } - } - - oldDescriptor.valueParameters.mapIndexedTo(this) { i, oldValueParameterDescriptor -> - createUnsubstitutedParameter(newDescriptor, oldValueParameterDescriptor, closureParametersCount + i).apply { - localFunctionContext.recordRemapped(oldValueParameterDescriptor, this) - } - } - } - - newDescriptor.initialize( - oldDescriptor.extensionReceiverParameter?.type, - newDispatchReceiverParameter, - newTypeParameters, - newValueParameters, - oldDescriptor.returnType, - Modality.FINAL, - Visibilities.PRIVATE - ) - - oldDescriptor.extensionReceiverParameter?.let { - localFunctionContext.recordRemapped(it, newDescriptor.extensionReceiverParameter!!) - } - - return newDescriptor - } - - private fun LocalFunctionContext.recordRemapped(oldDescriptor: ValueDescriptor, newDescriptor: ValueDescriptor): ValueDescriptor { - old2new[oldDescriptor] = newDescriptor - new2old[newDescriptor] = oldDescriptor - return newDescriptor - } - - private fun suggestNameForCapturedValueParameter(valueDescriptor: ValueDescriptor): Name = - if (valueDescriptor.name.isSpecial) { - val oldNameStr = valueDescriptor.name.asString() - Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1)) - } - else - valueDescriptor.name - - private fun createUnsubstitutedCapturedValueParameter( - newParameterOwner: CallableMemberDescriptor, - valueDescriptor: ValueDescriptor, - index: Int - ): ValueParameterDescriptor = - ValueParameterDescriptorImpl( - newParameterOwner, null, index, - valueDescriptor.annotations, - suggestNameForCapturedValueParameter(valueDescriptor), - valueDescriptor.type, - false, false, false, null, valueDescriptor.source - ) - - private fun createUnsubstitutedParameter( - newParameterOwner: CallableMemberDescriptor, - valueParameterDescriptor: ValueParameterDescriptor, - newIndex: Int - ): ValueParameterDescriptor = - valueParameterDescriptor.copy(newParameterOwner, valueParameterDescriptor.name, newIndex) - - - private fun collectClosures() { - memberFunction.acceptChildrenVoid(object : AbstractClosureAnnotator() { - override fun visitClass(declaration: IrClass) { - // ignore local classes for now - return - } - - override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) { - localFunctions[functionDescriptor]?.closure = closure - } - - override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) { - // ignore local classes for now - } - }) - } - - private fun collectLocalFunctions() { - memberFunction.acceptChildrenVoid(object : IrElementVisitorVoid { - var lambdasCount = 0 - - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFunction(declaration: IrFunction) { - declaration.acceptChildrenVoid(this) - val localFunctionContext = LocalFunctionContext(declaration) - localFunctions[declaration.descriptor] = localFunctionContext - if (declaration.descriptor.name.isSpecial) { - localFunctionContext.index = lambdasCount++ - } - } - - override fun visitClass(declaration: IrClass) { - // ignore local classes for now - } - }) - } - } - -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index 1a4622b4c9e..63af6735eb1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -5,16 +5,13 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.types.KotlinType -class IrLoweringContext(backendContext: BackendContext) : GeneratorContext { - override val irBuiltIns: IrBuiltIns = backendContext.irBuiltIns -} +class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns) -class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : GeneratorWithScope { +class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope { override val context = IrLoweringContext(backendContext) override val scope = Scope(functionDescriptor) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/SharedVariablesLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/SharedVariablesLowering.kt deleted file mode 100644 index c3996d208c7..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/SharedVariablesLowering.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.lower - -import org.jetbrains.kotlin.backend.common.BackendContext -import org.jetbrains.kotlin.backend.common.FunctionLoweringPass -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ValueDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrVariable -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrGetValue -import org.jetbrains.kotlin.ir.expressions.IrSetVariable -import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression -import org.jetbrains.kotlin.ir.visitors.* -import java.util.* - -class SharedVariablesLowering(val context: BackendContext) : FunctionLoweringPass { - override fun lower(irFunction: IrFunction) { - SharedVariablesTransformer(irFunction).lowerSharedVariables() - } - - private inner class SharedVariablesTransformer(val irFunction: IrFunction) { - val sharedVariables = HashSet() - - fun lowerSharedVariables() { - collectSharedVariables() - if (sharedVariables.isEmpty()) return - - rewriteSharedVariables() - } - - private fun collectSharedVariables() { - irFunction.acceptVoid(object : IrElementVisitorVoid { - val declarationsStack = ArrayDeque() - val currentDeclaration: DeclarationDescriptor - get() = declarationsStack.peek().descriptor - - val relevantVars = HashSet() - - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFunction(declaration: IrFunction) { - declarationsStack.push(declaration) - declaration.acceptChildrenVoid(this) - declarationsStack.pop() - } - - override fun visitVariable(declaration: IrVariable) { - declaration.acceptChildrenVoid(this) - - val variableDescriptor = declaration.descriptor - if (variableDescriptor.isVar) { - relevantVars.add(variableDescriptor) - } - } - - override fun visitVariableAccess(expression: IrValueAccessExpression) { - expression.acceptChildrenVoid(this) - - val descriptor = expression.descriptor - if (descriptor in relevantVars && descriptor.containingDeclaration != currentDeclaration) { - sharedVariables.add(descriptor) - } - } - }) - } - - private fun rewriteSharedVariables() { - val transformedDescriptors = HashMap() - - irFunction.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitVariable(declaration: IrVariable): IrStatement { - declaration.transformChildrenVoid(this) - - val oldDescriptor = declaration.descriptor - if (oldDescriptor !in sharedVariables) return declaration - - val newDescriptor = context.sharedVariablesManager.createSharedVariableDescriptor(oldDescriptor) - transformedDescriptors[oldDescriptor] = newDescriptor - - return context.sharedVariablesManager.defineSharedValue(newDescriptor, declaration) - } - }) - - irFunction.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitGetValue(expression: IrGetValue): IrExpression { - expression.transformChildrenVoid(this) - - val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression - - return context.sharedVariablesManager.getSharedValue(newDescriptor, expression) - } - - override fun visitSetVariable(expression: IrSetVariable): IrExpression { - expression.transformChildrenVoid(this) - - val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression - - return context.sharedVariablesManager.setSharedValue(newDescriptor, expression) - } - - private fun getTransformedDescriptor(oldDescriptor: ValueDescriptor): VariableDescriptor? = - transformedDescriptors.getOrElse(oldDescriptor) { - assert(oldDescriptor !in sharedVariables) { - "Shared variable is not transformed: $oldDescriptor" - } - null - } - }) - } - } -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index f841ea4dc52..67ebc05209e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -17,6 +17,10 @@ internal class KonanLower(val context: Context) { fun lower(irFile: IrFile) { val phaser = PhaseManager(context) + phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) { + DefaultParameterStubGenerator(context).runOnFilePostfix(irFile) + DefaultParameterInjector(context).runOnFilePostfix(irFile) + } phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) { BuiltinOperatorLowering(context).runOnFilePostfix(irFile) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index a678f76a973..2443f321cb6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -11,6 +11,7 @@ enum class KonanPhase(val description: String, /* */ BACKEND("All backend"), /* ... */ LOWER("IR Lowering"), /* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"), + /* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"), /* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"), /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"), /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt index af5e3455936..f0ced1ebbab 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPlatform.kt @@ -18,8 +18,8 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.backend.konan.KonanPlatformConfigurator -import org.jetbrains.kotlin.descriptors.PlatformKind import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.PlatformConfigurator import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.storage.StorageManager @@ -35,7 +35,7 @@ class KonanBuiltIns: KotlinBuiltIns { } object KonanPlatform : TargetPlatform("Konan") { - override val kind = PlatformKind.DEFAULT /* TODO: native */ + override val multiTargetPlatform = MultiTargetPlatform.Specific(platformName) override val defaultImports: List = Default.defaultImports + listOf( ImportPath("kotlin.*"), ImportPath("kotlin.collections.*"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt index aa127e69e81..5e5a3384db7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LinkStage.kt @@ -33,7 +33,7 @@ internal class MacOSPlatform(distrib: Distribution, override val linkerOptimizationFlags = properties.propertyList("linkerOptimizationFlags.osx") - override val linkerKonanFlags = listOf(distrib.libCppAbi) + override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx") override val linker = "${distrib.sysRoot}/usr/bin/ld" override fun linkCommand(objectFiles: List, executable: String, optimize: Boolean): List { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt index 676ac2c42d1..af7f8770fed 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TopDownAnalyzerFacadeForKonan.kt @@ -31,7 +31,8 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProvid object TopDownAnalyzerFacadeForKonan { fun analyzeFiles(files: Collection, config: KonanConfig): AnalysisResult { - val context = ContextForNewModule(ProjectContext(config.project), Name.special("<${config.moduleId}>"), KonanPlatform.builtIns) + val context = ContextForNewModule(ProjectContext(config.project), Name.special("<${config.moduleId}>"), + KonanPlatform.builtIns, null) val builtinsForCompilerModule = KonanBuiltIns(context.storageManager, true) val compilerBuiltInsModule = builtinsForCompilerModule.builtInsModule diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index f0e09c14593..cbc1a30bf48 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -1,6 +1,8 @@ package org.jetbrains.kotlin.backend.konan.descriptors import org.jetbrains.kotlin.backend.konan.KonanBuiltIns +import org.jetbrains.kotlin.backend.konan.llvm.functionName +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation @@ -103,7 +105,7 @@ internal fun KonanBuiltIns.getKonanInternalClassOrNull(name: String): ClassDescr * @return built-in class `konan.internal.$name` */ internal fun KonanBuiltIns.getKonanInternalClass(name: String): ClassDescriptor = - getKonanInternalClassOrNull(name)!! + getKonanInternalClassOrNull(name) ?: TODO(name) internal fun KonanBuiltIns.getKonanInternalFunctions(name: String): List { return konanInternal.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).toList() @@ -149,3 +151,61 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean } internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit() + +internal val ClassDescriptor.сontributedMethods: List + get() { + val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors() + // (includes declarations from supers) + + val functions = contributedDescriptors.filterIsInstance() + + val properties = contributedDescriptors.filterIsInstance() + val getters = properties.mapNotNull { it.getter } + val setters = properties.mapNotNull { it.setter } + + val allMethods = (functions + getters + setters).sortedBy { + // TODO: use local hash instead, but it needs major refactoring. + it.functionName.hashCode() + } + return allMethods + } + +fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT + +// TODO: optimize +val ClassDescriptor.vtableEntries: List + get() { + assert(!this.isInterface) + + val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) { + emptyList() + } else { + this.getSuperClassOrAny().vtableEntries + } + + val methods = this.сontributedMethods + + val inheritedVtableSlots = superVtableEntries.map { superMethod -> + methods.single { OverridingUtil.overrides(it, superMethod) } + } + + return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable } + } + +val ClassDescriptor.vtableSize: Int + get() = if (this.isAbstract()) 0 else this.vtableEntries.size + +fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int { + this.vtableEntries.forEachIndexed { index, functionDescriptor -> + if (functionDescriptor == function.original) return index + } + throw Error(function.toString() + " not in vtable of " + this.toString()) +} + +val ClassDescriptor.methodTableEntries: List + get() { + assert (!this.isAbstract()) + + return this.сontributedMethods.filter { it.isOverridableOrOverrides } + // TODO: probably method table should contain all accessible methods to improve binary compatibility + } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 2609c23de9d..a792110a4d7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.konan.Distribution import org.jetbrains.kotlin.backend.konan.hash.GlobalHash import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.backend.konan.descriptors.backingField +import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -114,22 +115,24 @@ internal interface ContextUtils { return constValue(result) } + /** + * Pointer to struct { TypeInfo, vtable }. + */ + val ClassDescriptor.typeInfoWithVtable: ConstPointer + get() { + val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!) + return constPointer(externalGlobal(this.typeInfoSymbolName, type)) + } + + val ClassDescriptor.typeInfoPtr: ConstPointer + get() = typeInfoWithVtable.getElementPtr(0) + /** * Pointer to type info for given class. * It may be declared as pointer to external variable. */ val ClassDescriptor.llvmTypeInfoPtr: LLVMValueRef - get() { - val module = context.llvmModule - val globalName = this.typeInfoSymbolName - val globalPtr = LLVMGetNamedGlobal(module, globalName) ?: - LLVMAddGlobal(module, runtime.typeInfoType, globalName)!! - - return globalPtr - } - - val ClassDescriptor.typeInfoPtr: ConstPointer - get() = constPointer(this.llvmTypeInfoPtr) + get() = typeInfoPtr.llvm /** * Pointer to type info for this type, or `null` if the type doesn't have corresponding type info. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 05e87172719..bd8d8d19a45 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1337,11 +1337,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateGetField(value: IrGetField): LLVMValueRef { context.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null) { - val thisPtr = instanceFieldAccessReceiver(value) + val thisPtr = evaluateExpression(value.receiver!!) return codegen.loadSlot( fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) } else { + assert (value.receiver == null) val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!! return codegen.loadSlot(ptr, value.descriptor.isVar()) } @@ -1362,26 +1363,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// - private fun instanceFieldAccessReceiver(expression: IrFieldAccessExpression): LLVMValueRef { - val receiverExpression = expression.receiver - if (receiverExpression != null) { - return evaluateExpression(receiverExpression) - } else { - val classDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor - return currentCodeContext.genGetValue(classDescriptor.thisAsReceiverParameter) - } - } - - //-------------------------------------------------------------------------// - private fun evaluateSetField(value: IrSetField): LLVMValueRef { context.log("evaluateSetField : ${ir2string(value)}") val valueToAssign = evaluateExpression(value.value) if (value.descriptor.dispatchReceiverParameter != null) { - val thisPtr = instanceFieldAccessReceiver(value) + val thisPtr = evaluateExpression(value.receiver!!) codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)) } else { + assert (value.receiver == null) val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName) codegen.storeAnyGlobal(valueToAssign, globalValue!!) } @@ -1741,7 +1731,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val typeInfo = codegen.typeInfoValue(containingClass) val allocHint = Int32(1).llvm val thisValue = if (containingClass.isArray) { - assert(args.size == 1 && args[0].type == int32Type) + assert(args.size >= 1 && args[0].type == int32Type) val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0]) call(context.llvm.allocArrayFunction, allocArrayInstanceArgs) } else { @@ -1854,14 +1844,30 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// fun callVirtual(descriptor: FunctionDescriptor, args: List): LLVMValueRef { - val thisI8PtrPtr = codegen.bitcast(kInt8PtrPtr, args[0]) // Cast "this (i8*)" to i8**. - val typeInfoI8Ptr = codegen.load(thisI8PtrPtr) // Load TypeInfo address. - val typeInfoPtr = codegen.bitcast(codegen.kTypeInfoPtr, typeInfoI8Ptr) // Cast TypeInfo (i8*) to TypeInfo*. - val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked - val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup - val llvmMethod = call(context.llvm.lookupOpenMethodFunction, - lookupArgs) // Get method ptr to be invoked + val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!! + val typeInfoPtr = codegen.load(typeInfoPtrPtr) + assert (typeInfoPtr.type == codegen.kTypeInfoPtr) + val owner = descriptor.containingDeclaration as ClassDescriptor + val llvmMethod = if (!owner.isInterface) { + // If this is a virtual method of the class - we can call via vtable. + val index = owner.vtableIndex(descriptor) + + val vtablePlace = codegen.gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1 + val vtable = codegen.bitcast(kInt8PtrPtr, vtablePlace) + + val slot = codegen.gep(vtable, Int32(index).llvm) + codegen.load(slot) + } else { + // Otherwise, call via hashtable. + // TODO: optimize by storing interface number in lower bits of 'this' pointer + // when passing object as an interface. This way we can use those bits as index + // for an additional per-interface vtable. + val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked + val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup + call(context.llvm.lookupOpenMethodFunction, + lookupArgs) + } val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked val function = codegen.bitcast(functionPtrType, llvmMethod) // Cast method address to the type return call(descriptor, function, args) // Invoke the method diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index 0f29071c24c..6a8272a8a5d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -150,8 +150,9 @@ internal fun structType(types: List): LLVMTypeRef = memScoped { } internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef { - val returnType = if (function is ConstructorDescriptor) voidType else getLLVMReturnType(function.returnType!!) - val paramTypes = ArrayList(function.allValueParameters.map { getLLVMType(it.type) }) + val original = function.original + val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!) + val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) }) if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) memScoped { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt index d2418b967f6..bb0fb5c62b0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt @@ -1,13 +1,15 @@ package org.jetbrains.kotlin.backend.konan.llvm -import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.backend.konan.descriptors.isInterface +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassKind.* -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -21,7 +23,7 @@ private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppR fun typeToHashString(type: KotlinType): String { if (TypeUtils.isTypeParameter(type)) return "GENERIC" - var hashString = type.constructor.toString() + var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString() if (!type.arguments.isEmpty()) { hashString += "<${type.arguments.map { typeToHashString(it.type) @@ -94,4 +96,4 @@ internal val ClassDescriptor.typeInfoSymbolName: String get() = "ktype:" + this.fqNameSafe.toString() internal val PropertyDescriptor.symbolName:String -get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}" +get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}" \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 4bbe52711dc..f50e23e5858 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -4,16 +4,12 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.descriptors.implementation -import org.jetbrains.kotlin.backend.konan.descriptors.implementedInterfaces -import org.jetbrains.kotlin.backend.konan.descriptors.isInterface +import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny @@ -31,7 +27,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val objOffsetsCount: Int, val interfaces: ConstValue, val interfacesCount: Int, - val vtable: ConstValue, val methods: ConstValue, val methodsCount: Int, val fields: ConstValue, @@ -50,8 +45,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { interfaces, Int32(interfacesCount), - vtable, - methods, Int32(methodsCount), @@ -71,46 +64,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { return classType } - // TODO: optimize - private fun getVtableEntries(classDesc: ClassDescriptor): List { - assert (!classDesc.isInterface) - - val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDesc)) { - emptyList() - } else { - getVtableEntries(classDesc.getSuperClassOrAny()) - } - - val methods = classDesc.getContributedMethods() // TODO: ensure order is well-defined - - val inheritedVtableSlots = superVtableEntries.map { superMethod -> - methods.single { OverridingUtil.overrides(it, superMethod) } - } - - return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable } - } - - private fun getMethodTableEntries(classDesc: ClassDescriptor): List { - assert (classDesc.modality != Modality.ABSTRACT) - - return classDesc.getContributedMethods().filter { it.isOverridableOrOverrides } - // TODO: probably method table should contain all accessible methods to improve binary compatibility - } - - private fun ClassDescriptor.getContributedMethods(): List { - val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors() - // (includes declarations from supers) - - val functions = contributedDescriptors.filterIsInstance() - - val properties = contributedDescriptors.filterIsInstance() - val getters = properties.mapNotNull { it.getter } - val setters = properties.mapNotNull { it.setter } - - val allMethods = functions + getters + setters - return allMethods - } - private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) { val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo")) if (annot != null) { @@ -181,26 +134,27 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields) - val vtable: List + val vtableEntries: List val methods: List - if (classDesc.modality != Modality.ABSTRACT) { + if (!classDesc.isAbstract()) { // TODO: compile-time resolution limits binary compatibility - vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress } + vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress } - methods = getMethodTableEntries(classDesc).map { + methods = classDesc.methodTableEntries.map { val nameSignature = it.functionName.localHash // TODO: compile-time resolution limits binary compatibility val methodEntryPoint = it.implementation.entryPointAddress MethodTableRecord(nameSignature, methodEntryPoint) }.sortedBy { it.nameSignature.value } } else { - vtable = emptyList() + vtableEntries = emptyList() methods = emptyList() } + assert (vtableEntries.size == classDesc.vtableSize) - val vtablePtr = staticData.placeGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable) + val vtable = ConstArray(int8TypePtr, vtableEntries) val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods) @@ -209,15 +163,14 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { superType, objOffsetsPtr, objOffsets.size, interfacesPtr, interfaces.size, - vtablePtr, methodsPtr, methods.size, fieldsPtr, if (classDesc.isInterface) -1 else fields.size) - val typeInfoGlobal = classDesc.llvmTypeInfoPtr // TODO: it is a hack - LLVMSetInitializer(typeInfoGlobal, typeInfo.llvm) + val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack + LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm) LLVMSetGlobalConstant(typeInfoGlobal, 1) - exportTypeInfoIfRequired(classDesc, typeInfoGlobal) + exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 79ed2578c40..51994a3c352 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -9,9 +9,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.unboundCallableReferenceTy import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrTypeOperator -import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -120,27 +118,42 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } override fun IrExpression.useAs(type: KotlinType): IrExpression { - return this.adaptIfNecessary(type) + + val actualType = when (this) { + is IrCall -> this.descriptor.original.returnType ?: this.type + is IrGetField -> this.descriptor.original.type + else -> this.type + } + + return this.adaptIfNecessary(actualType, type) } - private fun IrExpression.adaptIfNecessary(expectedType: KotlinType): IrExpression { - val thisRepresentation = getCustomRepresentation(this.type) + override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression { + return this.useAsValue(parameter.original) + } + + override fun IrExpression.useForField(field: PropertyDescriptor): IrExpression { + return this.useForVariable(field.original) + } + + private fun IrExpression.adaptIfNecessary(actualType: KotlinType, expectedType: KotlinType): IrExpression { + val actualRepresentation = getCustomRepresentation(actualType) val expectedRepresentation = getCustomRepresentation(expectedType) return when { - thisRepresentation == expectedRepresentation -> this + actualRepresentation == expectedRepresentation -> this - thisRepresentation == null && expectedRepresentation != null -> { + actualRepresentation == null && expectedRepresentation != null -> { // This may happen in the following cases: - // 1. `this.type` is `Nothing`; - // 2. `this` has the incompatible type. + // 1. `actualType` is `Nothing`; + // 2. `actualType` is incompatible. this.unbox(expectedRepresentation) } - thisRepresentation != null && expectedRepresentation == null -> this.box(thisRepresentation) + actualRepresentation != null && expectedRepresentation == null -> this.box(actualRepresentation) - else -> throw IllegalArgumentException("this is ${this.type}, expected $expectedType") + else -> throw IllegalArgumentException("actual type is $actualType, expected $expectedType") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt index 364148f6ef1..3e0166a2f5b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt @@ -33,19 +33,6 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf private val builtIns = context.builtIns private val irBuiltins = context.irModule!!.irBuiltins - override fun visitTry(aTry: IrTry): IrExpression { - // Workaround for the bug in IrTryImpl.transformChildren: transform `finallyExpression` too. - // TODO: fix the bug and remove it. - val transformer = this - return (aTry as IrTryImpl).apply { - tryResult = tryResult.transform(transformer, null) - catches.forEachIndexed { i, irCatch -> - catches[i] = irCatch.transform(transformer, null) - } - finallyExpression = finallyExpression?.transform(transformer, null) - } - } - override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt index 43ab3009303..f110aeb70f1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt @@ -201,10 +201,10 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering, index = index + 1, annotations = Annotations.EMPTY, name = param.name, - outType = param.type, + outType = builtIns.nullableAnyType, // Use erased type. declaresDefaultValue = false, isCrossinline = false, isNoinline = false, - varargElementType = (param as? ValueParameterDescriptor)?.varargElementType, + varargElementType = null, source = SourceElement.NO_SOURCE) } @@ -223,8 +223,10 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering, val newValueParameters = listOf(functionParameter) + newUnboundParams + val newReturnType = builtIns.nullableAnyType // Use erased type. + newDescriptor.initialize(null, null, descriptor.typeParameters, newValueParameters, - descriptor.returnType, Modality.FINAL, Visibilities.PRIVATE) + newReturnType, Modality.FINAL, Visibilities.PRIVATE) return newDescriptor } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt new file mode 100644 index 00000000000..ae8a6f3fc75 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt @@ -0,0 +1,269 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanPlatform +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.addToStdlib.singletonList + +class DefaultParameterStubGenerator internal constructor(val context: Context): DeclarationContainerLoweringPass { + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.transformFlat { memberDeclaration -> + if (memberDeclaration is IrFunction) + lower(memberDeclaration) + else + null + } + + } + + object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER : + IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT") + + private fun lower(irFunction: IrFunction): List { + val bodies = mutableListOf() + irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{ + override fun visitExpressionBody(body: IrExpressionBody) { + bodies.add(body) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + }) + + val functionDescriptor = irFunction.descriptor + if (bodies.isNotEmpty()) { + val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() + val generator = context.createFunctionIrGenerator(descriptor) + val builder = generator.createIrBuilder() + builder.apply { + startOffset = irFunction.startOffset + endOffset = irFunction.endOffset + } + val body = generator.irBlockBody { + val params = mutableListOf() + val variables = mutableMapOf() + + for (valueParameter in functionDescriptor.valueParameters) { + val parameterDescriptor = descriptor.valueParameters[valueParameter.index] + if (valueParameter.hasDefaultValue()) { + val variable = scope.createTemporaryVariable( + irExpression = nullConst(valueParameter.type)!!, + isMutable = true) + val variableDescriptor = variable.descriptor + params.add(variableDescriptor) + +variable + val condition = irNotEquals(irCall(intAnd).apply { + dispatchReceiver = irGet(mask) + putValueArgument(0, irInt(1 shl valueParameter.index)) + }, irInt(0)) + val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter) + /* Use previously calculated values in next expression. */ + exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue): IrExpression { + if (!variables.containsKey(expression.descriptor)) + return expression + return irGet(variables[expression.descriptor] as VariableDescriptor) + } + }) + /* Mapping calculated values with its origin variables. */ + variables.put(valueParameter, variableDescriptor) + +irIfThenElse( + type = KonanPlatform.builtIns.unitType, + condition = condition, + thenPart = irSetVar(variableDescriptor, exprBody.expression), + elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor))) + + } else { + params.add(parameterDescriptor) + } + } + + irReturn(irCall(functionDescriptor).apply { + if (functionDescriptor.dispatchReceiverParameter != null) { + dispatchReceiver = irGet(dispatch!!) + } + if (functionDescriptor.extensionReceiverParameter != null) { + extensionReceiver = irGet(extension!!) + } + params.forEachIndexed { i, variable -> + putValueArgument(i, irGet(variable)) + } + }) + } + // TODO: replace irFunction with new one without expression bodies. + return listOf(irFunction, IrFunctionImpl( + irFunction.startOffset , + irFunction.endOffset, + DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + descriptor, body)) + } + return irFunction.singletonList() + } +} + +private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody { + return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!") +} + +private fun nullConst(type: KotlinType): IrExpression? { + when { + KotlinBuiltIns.isFloat(type) -> return IrConstImpl (0, 0, type, IrConstKind.Float, 0.0F) + KotlinBuiltIns.isDouble(type) -> return IrConstImpl (0, 0, type, IrConstKind.Double, 0.0) + KotlinBuiltIns.isBoolean(type) -> return IrConstImpl (0, 0, type, IrConstKind.Boolean, false) + KotlinBuiltIns.isByte(type) -> return IrConstImpl (0, 0, type, IrConstKind.Byte, 0) + KotlinBuiltIns.isShort(type) -> return IrConstImpl (0, 0, type, IrConstKind.Short, 0) + KotlinBuiltIns.isInt(type) -> return IrConstImpl (0, 0, type, IrConstKind.Int, 0) + KotlinBuiltIns.isLong(type) -> return IrConstImpl (0, 0, type, IrConstKind.Long, 0) + else -> return IrConstImpl(0, 0, type, IrConstKind.Null, null) + } +} + +class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass { + override fun lower(irBody: IrBody) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + super.visitCall(expression) + val descriptor = expression.descriptor + if (descriptor.valueParameters.none{it.hasDefaultValue()}) + return expression + var argumentsCount = 0 + expression.acceptChildrenVoid(object:IrElementVisitorVoid{ + override fun visitElement(element: IrElement) { + argumentsCount++ + } + }) + if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) + argumentsCount-- + if (argumentsCount == descriptor.valueParameters.size) + return expression + var maskValue = 0 + val functionDescriptor = descriptor as FunctionDescriptor + val (defaultFunctiondescriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() + val params = descriptor.valueParameters.mapIndexed { i, it -> + if (expression.getValueArgument(i) == null) maskValue = maskValue or (1 shl i) + val valueParameterDescriptor = defaultFunctiondescriptor.valueParameters[i] + return@mapIndexed valueParameterDescriptor to (expression.getValueArgument(i) ?: nullConst(valueParameterDescriptor.type)) + } + (mask to IrConstImpl( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = KonanPlatform.builtIns.intType, + kind = IrConstKind.Int, + value = maskValue)) + return IrCallImpl( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = descriptor.returnType!!, + descriptor = defaultFunctiondescriptor, + typeArguments = null) + .apply { + params.forEach { + putValueArgument(it.first.index, it.second) + } + extension?.apply { + putValueArgument(extension.index, expression.extensionReceiver) + } + dispatch?.apply { + putValueArgument(dispatch.index, expression.dispatchReceiver) + } + } + } + }) + } + +} + +val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType) +val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and")) + +data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor, + val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?) + +fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor { + val name = Name.identifier("${this.name.asString()}\$default") + val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY, + name, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE) + val maskVariable = valueParameter(descriptor, valueParameters.size, "__\$mask\$__", KonanPlatform.builtIns.intType) + + var extensionReceiver:ValueParameterDescriptor? = null + var index = valueParameters.size + 1 + extensionReceiverParameter?.let { + extensionReceiver = valueParameter(descriptor, index++, "__\$ext_receiver\$__", extensionReceiverParameter!!.type) + } + var dispatchReceiver:ValueParameterDescriptor? = null + dispatchReceiverParameter?.let { + dispatchReceiver = valueParameter(descriptor, index++, "__\$dispatch_receiver\$__", dispatchReceiverParameter!!.type) + } + + val parameterList = mutableListOf(*valueParameters.map{ + if (it.hasDefaultValue()) ValueParameterDescriptorImpl( + containingDeclaration = it.containingDeclaration, + original = it.original, + index = it.index, + annotations = it.annotations, + name = it.name, + outType = it.type, + declaresDefaultValue = false, + isCrossinline = it.isCrossinline, + isNoinline = it.isNoinline, + varargElementType = it.varargElementType, + source = it.source) + else it + }.toTypedArray()) + + parameterList.add(maskVariable) + if (extensionReceiver != null) parameterList.add(extensionReceiver) + if (dispatchReceiver != null) parameterList.add(dispatchReceiver) + descriptor.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameterType = */ null, + /* typeParameters = */ typeParameters, + /* unsubstitutedValueParameters = */ parameterList, + /* unsubstitutedReturnType = */ returnType, + /* modality = */ this.modality, + /* visibility = */ this.visibility) + return DefaultParameterDescriptor(descriptor, maskVariable, extensionReceiver, dispatchReceiver) +} + +private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor { + return ValueParameterDescriptorImpl( + containingDeclaration = descriptor, + original = null, + index = index, + annotations = Annotations.EMPTY, + name = Name.identifier(name), + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE + ) +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt deleted file mode 100644 index b11a7bdc99a..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.types.KotlinType - - -inline fun IrBuilderWithScope.irLet( - value: IrExpression, - origin: IrStatementOrigin? = null, - nameHint: String? = null, - body: (VariableDescriptor) -> IrExpression -): IrExpression { - val irTemporary = scope.createTemporaryVariable(value, nameHint) - val irResult = body(irTemporary.descriptor) - val irBlock = IrBlockImpl(startOffset, endOffset, irResult.type, origin) - irBlock.statements.add(irTemporary) - irBlock.statements.add(irResult) - return irBlock -} - -fun IrStatementsBuilder.defineTemporary(value: IrExpression, nameHint: String? = null): VariableDescriptor { - val temporary = scope.createTemporaryVariable(value, nameHint) - +temporary - return temporary.descriptor -} - -fun IrStatementsBuilder.defineTemporaryVar(value: IrExpression, nameHint: String? = null): VariableDescriptor { - val temporary = scope.createTemporaryVariable(value, nameHint, isMutable = true) - +temporary - return temporary.descriptor -} - -fun IrBuilderWithScope.irReturn(value: IrExpression) = - IrReturnImpl(startOffset, endOffset, context.builtIns.nothingType, scope.assertCastOwner(), value) - -fun IrBuilderWithScope.irReturnTrue() = - irReturn(IrConstImpl(startOffset, endOffset, context.builtIns.booleanType, IrConstKind.Boolean, true)) - -fun IrBuilderWithScope.irReturnFalse() = - irReturn(IrConstImpl(startOffset, endOffset, context.builtIns.booleanType, IrConstKind.Boolean, false)) - -fun IrBuilderWithScope.irIfThenElse(type: KotlinType, condition: IrExpression, thenPart: IrExpression, elsePart: IrExpression) = - IrIfThenElseImpl(startOffset, endOffset, type, condition, thenPart, elsePart) - -fun IrBuilderWithScope.irIfNull(type: KotlinType, subject: IrExpression, thenPart: IrExpression, elsePart: IrExpression) = - irIfThenElse(type, irEqualsNull(subject), thenPart, elsePart) - -fun IrBuilderWithScope.irThrowNpe(origin: IrStatementOrigin) = - IrNullaryPrimitiveImpl(startOffset, endOffset, origin, context.irBuiltIns.throwNpe) - -fun IrBuilderWithScope.irIfThenReturnTrue(condition: IrExpression) = - IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, irReturnTrue()) - -fun IrBuilderWithScope.irIfThenReturnFalse(condition: IrExpression) = - IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, irReturnFalse()) - -fun IrBuilderWithScope.irThis() = - scope.classOwner().let { classOwner -> - IrGetValueImpl(startOffset, endOffset, classOwner.thisAsReceiverParameter) - } - -fun IrBuilderWithScope.irGet(variable: VariableDescriptor) = - IrGetValueImpl(startOffset, endOffset, variable) - -fun IrBuilderWithScope.irSetVar(variable: VariableDescriptor, value: IrExpression) = - IrSetVariableImpl(startOffset, endOffset, variable, value, IrStatementOrigin.EQ) - -fun IrBuilderWithScope.irOther() = - irGet(scope.functionOwner().valueParameters.single()) - -fun IrBuilderWithScope.irEqeqeq(arg1: IrExpression, arg2: IrExpression) = - context.eqeqeq(startOffset, endOffset, arg1, arg2) - -fun IrBuilderWithScope.irNull() = - IrConstImpl.constNull(startOffset, endOffset, context.builtIns.nullableNothingType) - -fun IrBuilderWithScope.irEqualsNull(argument: IrExpression) = - primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrStatementOrigin.EQEQ, - argument, irNull()) - -fun IrBuilderWithScope.irNotEquals(arg1: IrExpression, arg2: IrExpression) = - primitiveOp1(startOffset, endOffset, context.irBuiltIns.booleanNot, IrStatementOrigin.EXCLEQ, - primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrStatementOrigin.EXCLEQ, - arg1, arg2)) - -fun IrBuilderWithScope.irGet(receiver: IrExpression, property: PropertyDescriptor): IrExpression = - IrGetterCallImpl(startOffset, endOffset, property.getter!!, null, receiver, null, IrStatementOrigin.GET_PROPERTY) - -fun IrBuilderWithScope.irCall(callee: CallableDescriptor) = - IrCallImpl(startOffset, endOffset, callee.returnType!!, callee, null) - -fun IrBuilderWithScope.irCallOp(callee: CallableDescriptor, dispatchReceiver: IrExpression, argument: IrExpression) = - IrCallImpl(startOffset, endOffset, callee.returnType!!, callee, null).apply { - this.dispatchReceiver = dispatchReceiver - putValueArgument(0, argument) - } - -fun IrBuilderWithScope.irIs(argument: IrExpression, type: KotlinType) = - IrTypeOperatorCallImpl(startOffset, endOffset, context.builtIns.booleanType, IrTypeOperator.INSTANCEOF, type, argument) - -fun IrBuilderWithScope.irNotIs(argument: IrExpression, type: KotlinType) = - IrTypeOperatorCallImpl(startOffset, endOffset, context.builtIns.booleanType, IrTypeOperator.NOT_INSTANCEOF, type, argument) - -fun IrBuilderWithScope.irAs(argument: IrExpression, type: KotlinType) = - IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.CAST, type, argument) - -fun IrBuilderWithScope.irImplicitCast(argument: IrExpression, type: KotlinType) = - IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.IMPLICIT_CAST, type, argument) - -fun IrBuilderWithScope.irInt(value: Int) = - IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, value) - -fun IrBuilderWithScope.irString(value: String) = - IrConstImpl.string(startOffset, endOffset, context.builtIns.stringType, value) - -fun IrBuilderWithScope.irConcat() = - IrStringConcatenationImpl(startOffset, endOffset, context.builtIns.stringType) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Generator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Generator.kt deleted file mode 100644 index 934420e8df1..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Generator.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns - -// TODO: rename Generator* to IrGenerator* when contributing back to Kotlin. - -interface Generator { - val context: GeneratorContext -} - -interface GeneratorWithScope : Generator { - val scope: Scope -} - -interface GeneratorContext { - val irBuiltIns: IrBuiltIns - val builtIns: KotlinBuiltIns get() = irBuiltIns.builtIns -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt deleted file mode 100644 index 19123a64b98..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl -import org.jetbrains.kotlin.types.KotlinType -import java.util.* - -abstract class IrBuilder( - override val context: GeneratorContext, - var startOffset: Int, - var endOffset: Int -) : Generator - -abstract class IrBuilderWithScope( - context: GeneratorContext, - override val scope: Scope, - startOffset: Int, - endOffset: Int -) : IrBuilder(context, startOffset, endOffset), GeneratorWithScope - -abstract class IrStatementsBuilder( - context: GeneratorContext, - scope: Scope, - startOffset: Int, - endOffset: Int -) : IrBuilderWithScope(context, scope, startOffset, endOffset) { - operator fun IrStatement.unaryPlus() { - addStatement(this) - } - - protected abstract fun addStatement(irStatement: IrStatement) - protected abstract fun doBuild(): T -} - -open class IrBlockBodyBuilder( - context: GeneratorContext, - scope: Scope, - startOffset: Int, - endOffset: Int -) : IrStatementsBuilder(context, scope, startOffset, endOffset) { - private val irBlockBody = IrBlockBodyImpl(startOffset, endOffset) - - inline fun blockBody(body: IrBlockBodyBuilder.() -> Unit): IrBlockBody { - body() - return doBuild() - } - - override fun addStatement(irStatement: IrStatement) { - irBlockBody.statements.add(irStatement) - } - - override fun doBuild(): IrBlockBody { - return irBlockBody - } -} - -class IrBlockBuilder( - context: GeneratorContext, scope: Scope, - startOffset: Int, endOffset: Int, - val origin: IrStatementOrigin? = null, - var resultType: KotlinType? = null -) : IrStatementsBuilder(context, scope, startOffset, endOffset) { - private val statements = ArrayList() - - inline fun block(body: IrBlockBuilder.() -> Unit): IrBlock { - body() - return doBuild() - } - - override fun addStatement(irStatement: IrStatement) { - statements.add(irStatement) - } - - override fun doBuild(): IrBlock { - val resultType = this.resultType ?: - (statements.lastOrNull() as? IrExpression)?.type ?: - context.builtIns.unitType - val irBlock = IrBlockImpl(startOffset, endOffset, resultType, origin) - irBlock.statements.addAll(statements) - return irBlock - } -} - -fun T.at(startOffset: Int, endOffset: Int): T { - this.startOffset = startOffset - this.endOffset = endOffset - return this -} - -inline fun GeneratorWithScope.irBlock(startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET, - origin: IrStatementOrigin? = null, - resultType: KotlinType? = null, - body: IrBlockBuilder.() -> Unit -): IrExpression = - IrBlockBuilder(context, scope, - startOffset, - endOffset, - origin, resultType - ).block(body) - -inline fun GeneratorWithScope.irBlockBody(startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET, - body: IrBlockBodyBuilder.() -> Unit -) : IrBlockBody = - IrBlockBodyBuilder(context, scope, - startOffset, - endOffset - ).blockBody(body) \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrMemberFunctionBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrMemberFunctionBuilder.kt deleted file mode 100644 index 70e0bca79c3..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/IrMemberFunctionBuilder.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl - -class IrMemberFunctionBuilder( - context: GeneratorContext, - val irClass: IrClassImpl, - val function: FunctionDescriptor, - val origin: IrDeclarationOrigin, - startOffset: Int = UNDEFINED_OFFSET, - endOffset: Int = UNDEFINED_OFFSET -) : IrBlockBodyBuilder(context, Scope(function), startOffset, endOffset) { - inline fun addToClass(body: IrMemberFunctionBuilder.() -> Unit) { - val irFunction = IrFunctionImpl(startOffset, endOffset, origin, function) - body() - irFunction.body = doBuild() - irClass.addMember(irFunction) - } -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Primitives.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Primitives.kt deleted file mode 100644 index 16339317d3d..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Primitives.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* - -fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, origin: IrStatementOrigin, - argument: IrExpression): IrExpression = - IrUnaryPrimitiveImpl(startOffset, endOffset, origin, primitiveOpDescriptor, argument) - -fun primitiveOp2(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, origin: IrStatementOrigin, - argument1: IrExpression, argument2: IrExpression): IrExpression = - IrBinaryPrimitiveImpl(startOffset, endOffset, origin, primitiveOpDescriptor, argument1, argument2) - -fun GeneratorContext.constNull(startOffset: Int, endOffset: Int): IrExpression = - IrConstImpl.constNull(startOffset, endOffset, builtIns.nullableNothingType) - -fun GeneratorContext.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression = - primitiveOp2(startOffset, endOffset, irBuiltIns.eqeq, IrStatementOrigin.EQEQ, - argument, constNull(startOffset, endOffset)) - -fun GeneratorContext.eqeqeq(startOffset: Int, endOffset: Int, argument1: IrExpression, argument2: IrExpression): IrExpression = - primitiveOp2(startOffset, endOffset, irBuiltIns.eqeqeq, IrStatementOrigin.EQEQEQ, argument1, argument2) - -fun GeneratorContext.throwNpe(startOffset: Int, endOffset: Int, origin: IrStatementOrigin): IrExpression = - IrNullaryPrimitiveImpl(startOffset, endOffset, origin, irBuiltIns.throwNpe) - -// a || b == if (a) true else b -fun GeneratorContext.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.OROR): IrWhen = - IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType, - a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type), b, - origin) - -fun GeneratorContext.oror(a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.OROR): IrWhen = - oror(b.startOffset, b.endOffset, a, b, origin) - -fun GeneratorContext.whenComma(a: IrExpression, b: IrExpression): IrWhen = - oror(a, b, IrStatementOrigin.WHEN_COMMA) - -// a && b == if (a) b else false -fun GeneratorContext.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.ANDAND): IrWhen = - IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType, - a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type), - origin) - -fun GeneratorContext.andand(a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.ANDAND): IrWhen = - andand(b.startOffset, b.endOffset, a, b, origin) \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Scope.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Scope.kt deleted file mode 100644 index 6b12392c193..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/Scope.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrVariable -import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl -import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor -import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.types.KotlinType - -class Scope(val scopeOwner: DeclarationDescriptor) { - private var lastTemporaryIndex: Int = 0 - private fun nextTemporaryIndex(): Int = lastTemporaryIndex++ - - private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null, isMutable: Boolean = false): IrTemporaryVariableDescriptor = - IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type, isMutable) - - private fun getNameForTemporary(nameHint: String?): String { - val index = nextTemporaryIndex() - return if (nameHint != null) "tmp${index}_$nameHint" else "tmp$index" - } - - fun createTemporaryVariable(irExpression: IrExpression, nameHint: String? = null, isMutable: Boolean = false): IrVariable = - IrVariableImpl( - irExpression.startOffset, irExpression.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, - createDescriptorForTemporaryVariable(irExpression.type, nameHint, isMutable), - irExpression - ) -} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ScopeHelpers.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ScopeHelpers.kt deleted file mode 100644 index 9cf20261705..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/builders/ScopeHelpers.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders - -import org.jetbrains.kotlin.descriptors.* - -inline fun Scope.assertCastOwner() = - scopeOwner as? T ?: - throw AssertionError("Unexpected scopeOwner: $scopeOwner") - -fun Scope.functionOwner(): FunctionDescriptor = - assertCastOwner() - -fun Scope.classOwner(): ClassDescriptor = - when (scopeOwner) { - is ClassDescriptor -> scopeOwner - is MemberDescriptor -> scopeOwner.containingDeclaration as ClassDescriptor - else -> throw AssertionError("Unexpected scopeOwner: $scopeOwner") - } diff --git a/backend.native/konan.properties b/backend.native/konan.properties index 8edf4e62b0f..c45907461ec 100644 --- a/backend.native/konan.properties +++ b/backend.native/konan.properties @@ -10,12 +10,13 @@ libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux- llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0 +linkerKonanFlags.osx = -lc++ linkerOptimizationFlags.osx = -dead_strip macosVersionMin.osx = 10.10.0 llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main llvmLlcFlags.linux = -march=x86-64 -linkerKonanFlags.linux = -Bstatic -lc++abi -Bdynamic -ldl -lm -lpthread +linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread linkerOptimizationFlags.linux = --gc-sections pluginOptimizationFlags.linux = -plugin-opt=mcpu=x86-64 -plugin-opt=O3 diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 1e0b15521aa..55147a82d03 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -41,11 +41,11 @@ abstract class KonanTest extends DefaultTask { project.javaexec { main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt' classpath = project.configurations.cli_bc - jvmArgs "-ea", + jvmArgs "-ea", "-Dkonan.home=${dist.canonicalPath}", "-Djava.library.path=${dist.canonicalPath}/konan/nativelib" args("-output", output, - source, + source, *moreArgs, *project.globalArgs) } @@ -88,7 +88,7 @@ abstract class KonanTest extends DefaultTask { class RunKonanTest extends KonanTest { void compileTest(String source, String exe) { - runCompiler(source, exe, []) + runCompiler(source, exe, []) } } @@ -98,7 +98,7 @@ class LinkKonanTest extends KonanTest { void compileTest(String source, String exe) { def libDir = project.file(lib).absolutePath def libBc = "${libDir}.bc" - + runCompiler(lib, libBc, ['-nolink', '-nostdlib']) runCompiler(source, exe, ['-library', libBc]) } @@ -149,6 +149,12 @@ task objectInitialization(type: RunKonanTest) { source = "codegen/object/initialization.kt" } +task objectInitialization1(type: RunKonanTest) { + disabled = true + goldValue = "init\nfield\nconstructor1\ninit\nfield\nconstructor1\nconstructor2\n" + source = "codegen/object/initialization1.kt" +} + task check_type(type: RunKonanTest) { goldValue = "true\nfalse\ntrue\ntrue\ntrue\ntrue\n" source = "codegen/basics/check_type.kt" @@ -173,6 +179,41 @@ task sum2(type: RunKonanTest) { source = "codegen/function/sum_imm.kt" } +task defaults(type: RunKonanTest) { + source = "codegen/function/defaults.kt" +} + +task defaults1(type: RunKonanTest) { + source = "codegen/function/defaults1.kt" +} + +task defaults2(type: RunKonanTest) { + source = "codegen/function/defaults2.kt" +} + +task defaults3(type: RunKonanTest) { + source = "codegen/function/defaults3.kt" +} + +task defaults4(type: RunKonanTest) { + disabled = true + goldValue = "43\n" + source = "codegen/function/defaults4.kt" +} + +task defaults5(type: RunKonanTest) { + disabled = true + goldValue = "5\n6\n" + source = "codegen/function/defaults5.kt" +} + +task defaults6(type: RunKonanTest) { + disabled = true + goldValue = "42\n" + source = "codegen/function/defaults6.kt" +} + + task sum_3const(type: RunKonanTest) { source = "codegen/function/sum_3const.kt" } @@ -195,7 +236,6 @@ task null_check(type: RunKonanTest) { } task array_to_any(type: RunKonanTest) { - disabled = true source = "codegen/basics/array_to_any.kt" } @@ -247,8 +287,8 @@ task tostring3(type: RunKonanTest) { "1.17549E-38\n3.40282E+38\n-INF\nINF\n" + // Linux version prints -NAN. // "NAN\n" + - "4.94066E-324\n1.79769E+308\n-INF\nINF\n" - // "NAN\n" + "4.94066E-324\n1.79769E+308\n-INF\nINF\n" + // "NAN\n" source = "runtime/basic/tostring3.kt" } @@ -267,6 +307,11 @@ task array1(type: RunKonanTest) { source = "runtime/collections/array1.kt" } +task array2(type: RunKonanTest) { + goldValue = "0\n2\n4\n6\n8\n40\n" + source = "runtime/collections/array2.kt" +} + task if_else(type: RunKonanTest) { source = "codegen/branching/if_else.kt" } @@ -308,6 +353,11 @@ task bool_yes(type: RunKonanTest) { source = "codegen/function/boolean.kt" } +task named(type: RunKonanTest) { + source = "codegen/function/named.kt" +} + + task plus_eq(type: RunKonanTest) { source = "codegen/function/plus_eq.kt" } @@ -360,7 +410,7 @@ task intrinsic(type: RunKonanTest) { } /* - Disabled until we extract the classes that should be + Disabled until we extract the classes that should be always present from stdlib.kt.bc into a separate binary. task link(type: LinkKonanTest) { @@ -386,7 +436,6 @@ task statements0(type: RunKonanTest) { } task boxing0(type: RunKonanTest) { - disabled = true goldValue = "17\n" source = "codegen/boxing/boxing0.kt" } @@ -498,6 +547,10 @@ task moderately_large_array(type: RunKonanTest) { source = "runtime/collections/moderately_large_array.kt" } +task moderately_large_array1(type: RunKonanTest) { + goldValue = "-45392\n" + source = "runtime/collections/moderately_large_array1.kt" +} task string_builder0(type: RunKonanTest) { goldValue = "OK\n" @@ -807,6 +860,11 @@ task memory_throw_cleanup(type: RunKonanTest) { source = "runtime/memory/throw_cleanup.kt" } +task memory_collect_cycles(type: RunKonanTest) { + goldValue = "42\n" + source = "runtime/memory/cycles0.kt" +} + task unit1(type: RunKonanTest) { goldValue = "First\nkotlin.Unit\n" source = "codegen/basics/unit1.kt" diff --git a/backend.native/tests/codegen/basics/array_to_any.kt b/backend.native/tests/codegen/basics/array_to_any.kt index 4961fee3d9e..e56ab313351 100644 --- a/backend.native/tests/codegen/basics/array_to_any.kt +++ b/backend.native/tests/codegen/basics/array_to_any.kt @@ -1,7 +1,7 @@ fun main(args: Array) { - println(foo().toString()) + foo().hashCode() } fun foo(): Any { - return Array(0) + return Array(0, { i -> null }) } \ No newline at end of file diff --git a/backend.native/tests/codegen/function/defaults.kt b/backend.native/tests/codegen/function/defaults.kt new file mode 100644 index 00000000000..a4562f3ddc5 --- /dev/null +++ b/backend.native/tests/codegen/function/defaults.kt @@ -0,0 +1,97 @@ +/** + * Created by minamoto on 12/26/16. + */ +//package defaults + +open class A(val a:Int) { + override fun equals(other: Any?): Boolean { + if (other == null || other as? A == null) return false + return (other as A).a == a // Where is smart casting? + } + + companion object { + val zero = A(0) + val one = A(1) + val magic = A(42) + } + + +} +// FUN public fun foo(a: defaults.A = ...): kotlin.Int +// a: EXPRESSION_BODY +// CALL '(): A' type=defaults.A origin=GET_PROPERTY +// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion +// BLOCK_BODY +// RETURN type=kotlin.Nothing from='foo(A = ...): Int' +// CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY +// $this: GET_VAR 'value-parameter a: A = ...' type=defaults.A origin=null +fun foo(a: A = A.magic, b:Int = 0xdeadbeef.toInt()) = a.a + +// FUN public fun bar(a: defaults.A, inc: kotlin.Int = ...): defaults.A +// inc: EXPRESSION_BODY +// CONST Int type=kotlin.Int value='0' +// BLOCK_BODY +// RETURN type=kotlin.Nothing from='bar(A, Int = ...): A' +// CALL 'constructor A(Int)' type=defaults.A origin=null +// a: CALL 'plus(Int): Int' type=kotlin.Int origin=PLUS +// $this: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY +// $this: GET_VAR 'value-parameter a: A' type=defaults.A origin=null +// other: GET_VAR 'value-parameter inc: Int = ...' type=kotlin.Int origin=null +fun bar(a:A, inc:Int = 0) = A(a.a + inc) + + +fun main(args:Array) { + +// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'foo(A = ...): Int' type=kotlin.Int origin=null +// arg1: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY +// $this: CALL '(): A' type=defaults.A origin=GET_PROPERTY +// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion + if (foo() != A.magic.a) { + println("magic failed") + throw Error() + } + +// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'foo(A = ...): Int' type=kotlin.Int origin=null +// a: CALL 'constructor A(Int)' type=defaults.A origin=null +// a: CONST Int type=kotlin.Int value='1' +// arg1: CONST Int type=kotlin.Int value='1' + if (foo(A(1)) != 1) { + println("one failed: foo(A(1))") + throw Error() + } + +// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'bar(A, Int = ...): A' type=defaults.A origin=null +// a: CALL '(): A' type=defaults.A origin=GET_PROPERTY <--- +// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion +// arg1: CALL '(): A' type=defaults.A origin=GET_PROPERTY +// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion + + if (bar(A.one) != A.one) { + println("A one failed") + throw Error() + } + + +// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ +// arg0: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY +// $this: CALL 'bar(A, Int = ...): A' type=defaults.A origin=null +// a: CALL '(): A' type=defaults.A origin=GET_PROPERTY +// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion +// inc: CONST Int type=kotlin.Int value='1' +// arg1: CONST Int type=kotlin.Int value='2' + if (bar(A.one, 1).a != 2) { + println("A one + 1 failed") + throw Error() + } + println("all tests passed") +} + + + diff --git a/backend.native/tests/codegen/function/defaults1.kt b/backend.native/tests/codegen/function/defaults1.kt new file mode 100644 index 00000000000..d8b2a04ab63 --- /dev/null +++ b/backend.native/tests/codegen/function/defaults1.kt @@ -0,0 +1,9 @@ +fun foo(x:Int = 0, y:Int = x + 1, z:Int = x + y + 1) = x + y + z + +fun main(arg:Array) { + val v = foo() + if (v != 3) { + println("test failed $v expected 3") + throw Error() + } +} \ No newline at end of file diff --git a/backend.native/tests/codegen/function/defaults2.kt b/backend.native/tests/codegen/function/defaults2.kt new file mode 100644 index 00000000000..8f6830471dc --- /dev/null +++ b/backend.native/tests/codegen/function/defaults2.kt @@ -0,0 +1,10 @@ + +fun Int.foo(inc0:Int, inc:Int = 0) = this + inc0 + inc + +fun main(arg:Array) { + val v = 42.foo(0) + if (v != 42) { + println("test failed v:$v expected:42") + throw Error() + } +} diff --git a/backend.native/tests/codegen/function/defaults3.kt b/backend.native/tests/codegen/function/defaults3.kt new file mode 100644 index 00000000000..9be86df16f0 --- /dev/null +++ b/backend.native/tests/codegen/function/defaults3.kt @@ -0,0 +1,12 @@ +fun foo(a:Int = 2, b:String = "Hello", c:Int = 4):String = "$b-$c$a" +fun foo(a:Int = 3, b:Int = a + 1, c:Int = a + b) = a + b + c + +fun main(arg:Array){ + val a = foo(b="Universe") + if (a != "Universe-42") + throw Error() + + val b = foo(b = 5) + if (b != (/* a = */ 3 + /* b = */ 5 + /* c = */ (3 + 5))) + throw Error() +} \ No newline at end of file diff --git a/backend.native/tests/codegen/function/defaults4.kt b/backend.native/tests/codegen/function/defaults4.kt new file mode 100644 index 00000000000..418bf1d35db --- /dev/null +++ b/backend.native/tests/codegen/function/defaults4.kt @@ -0,0 +1,11 @@ +open class A { + open fun foo(x: Int = 42) = println(x) +} + +class B : A() { + override fun foo(x: Int) = println(x + 1) +} + +fun main(args: Array) { + B().foo() +} \ No newline at end of file diff --git a/backend.native/tests/codegen/function/defaults5.kt b/backend.native/tests/codegen/function/defaults5.kt new file mode 100644 index 00000000000..cd57c59a36f --- /dev/null +++ b/backend.native/tests/codegen/function/defaults5.kt @@ -0,0 +1,14 @@ +class Test(val x: Int) { + fun foo(y: Int = x) { + println(y) + } +} + +fun Test.bar(y: Int = x) { + println(y) +} + +fun main(args: Array) { + Test(5).foo() + Test(6).bar() +} \ No newline at end of file diff --git a/backend.native/tests/codegen/function/defaults6.kt b/backend.native/tests/codegen/function/defaults6.kt new file mode 100644 index 00000000000..ed579700552 --- /dev/null +++ b/backend.native/tests/codegen/function/defaults6.kt @@ -0,0 +1,6 @@ +open class Foo(val x: Int = 42) +class Bar : Foo() + +fun main(args: Array) { + println(Bar().x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/function/named.kt b/backend.native/tests/codegen/function/named.kt new file mode 100644 index 00000000000..00d18ab29c4 --- /dev/null +++ b/backend.native/tests/codegen/function/named.kt @@ -0,0 +1,5 @@ +fun foo(a:Int, b:Int) = a - b +fun main(args:Array) { + if (foo(b = 24, a = 42) != 18) + throw Error() +} \ No newline at end of file diff --git a/backend.native/tests/codegen/object/initialization1.kt b/backend.native/tests/codegen/object/initialization1.kt new file mode 100644 index 00000000000..2756dbe536f --- /dev/null +++ b/backend.native/tests/codegen/object/initialization1.kt @@ -0,0 +1,20 @@ +class Test { + constructor() { + println("constructor1") + } + + constructor(x: Int) : this() { + println("constructor2") + } + + init { + println("init") + } + + val f = println("field") +} + +fun main(args: Array) { + Test() + Test(1) +} \ No newline at end of file diff --git a/backend.native/tests/runtime/collections/array0.kt b/backend.native/tests/runtime/collections/array0.kt index b555959cbcd..01fa4aeded9 100644 --- a/backend.native/tests/runtime/collections/array0.kt +++ b/backend.native/tests/runtime/collections/array0.kt @@ -24,6 +24,6 @@ fun main(args : Array) { val booleanArray = BooleanArray(12) println(booleanArray.size.toString()) - val stringArray = Array(13) + val stringArray = Array(13, { i -> ""}) println(stringArray.size.toString()) } \ No newline at end of file diff --git a/backend.native/tests/runtime/collections/array2.kt b/backend.native/tests/runtime/collections/array2.kt new file mode 100644 index 00000000000..edd2d3e1923 --- /dev/null +++ b/backend.native/tests/runtime/collections/array2.kt @@ -0,0 +1,7 @@ +fun main(args : Array) { + val byteArray = Array(5, { i -> (i * 2).toByte() }) + byteArray.map { println(it) } + + val intArray = Array(5, { i -> i * 4 }) + println(intArray.sum()) +} \ No newline at end of file diff --git a/backend.native/tests/runtime/collections/hash_map0.kt b/backend.native/tests/runtime/collections/hash_map0.kt index 426c2c15a30..37f3656b950 100644 --- a/backend.native/tests/runtime/collections/hash_map0.kt +++ b/backend.native/tests/runtime/collections/hash_map0.kt @@ -181,7 +181,6 @@ fun testPutEntry() { assertTrue(expected == m) } -/* Fails due to variance. fun testRemoveAllEntries() { val expected = mapOf("a" to "1", "b" to "2", "c" to "3") val m = HashMap(expected) @@ -199,7 +198,7 @@ fun testRetainAllEntries() { assertEquals(expected, m) assertTrue(m.entries.retainAll(mapOf("b" to "22", "c" to "3", "d" to "4").entries)) assertEquals(mapOf("c" to "3"), m) -} */ +} fun testContainsAllValues() { val m = HashMap(mapOf("a" to "1", "b" to "2", "c" to "3")) @@ -256,8 +255,8 @@ fun main(args : Array) { testHashCode() testToString() testPutEntry() - //testRemoveAllEntries() - //testRetainAllEntries() + testRemoveAllEntries() + testRetainAllEntries() testContainsAllValues() testRemoveValue() testRemoveAllValues() diff --git a/backend.native/tests/runtime/collections/moderately_large_array1.kt b/backend.native/tests/runtime/collections/moderately_large_array1.kt new file mode 100644 index 00000000000..06db38faf13 --- /dev/null +++ b/backend.native/tests/runtime/collections/moderately_large_array1.kt @@ -0,0 +1,11 @@ +fun main(args: Array) { + val a = Array(100000, { i -> i.toByte()}) + + var sum = 0 + for (b in a) { + sum += b + } + + println(sum) +} + diff --git a/backend.native/tests/runtime/memory/cycles0.kt b/backend.native/tests/runtime/memory/cycles0.kt new file mode 100644 index 00000000000..dc7872d1c09 --- /dev/null +++ b/backend.native/tests/runtime/memory/cycles0.kt @@ -0,0 +1,45 @@ +data class Node(val data: Int, var next: Node?, var prev: Node?, val outer: Node?) + +fun makeCycle(len: Int, outer: Node?): Node { + val start = Node(0, null, null, outer) + var prev = start + for (i in 1 .. len - 1) { + prev = Node(i, prev, null, outer) + } + start.next = prev + return start +} + +fun makeDoubleCycle(len: Int): Node { + val start = makeCycle(len, null) + var prev = start + var cur = prev.next + while (cur != start) { + cur!!.prev = prev + prev = cur + cur = cur.next + } + start.prev = prev + return start +} + +fun createCycles(junk: Node) { + val cycle1 = makeCycle(1, junk) + val cycle2 = makeCycle(2, junk) + val cycle10 = makeCycle(10, junk) + val cycle100 = makeCycle(100, junk) + val dcycle1 = makeDoubleCycle(1) + val dcycle2 = makeDoubleCycle(2) + val dcycle10 = makeDoubleCycle(10) + val dcycle100 = makeDoubleCycle(100) + +} + +fun main(args : Array) { + // Create outer link from cyclic garbage. + val outer = Node(42, null, null, null) + createCycles(outer) + konan.internal.GC.collect() + // Ensure outer is not collected. + println(outer.data) +} diff --git a/gradle.properties b/gradle.properties index f461d5a1f05..422a44c3a4b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ kotlin_version=1.1-M03 #kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-SNAPSHOT -kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-20161229.093306-344 +kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-20170116.100209-359 \ No newline at end of file diff --git a/runtime/src/main/cpp/Common.h b/runtime/src/main/cpp/Common.h index 69bcead4f24..287344d2193 100644 --- a/runtime/src/main/cpp/Common.h +++ b/runtime/src/main/cpp/Common.h @@ -2,5 +2,7 @@ #define RUNTIME_COMMON_H #define RUNTIME_NOTHROW __attribute__((nothrow)) +#define RUNTIME_CONST __attribute__((const)) +#define RUNTIME_PURE __attribute__((pure)) #endif // RUNTIME_COMMON_H diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 6ee6fd74ee8..c8da8601f31 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -3,7 +3,7 @@ #include #include // for offsetof -#include // only for memory tracing. +#include #include #include "Assert.h" @@ -11,44 +11,193 @@ #include "Memory.h" #include "Natives.h" -// Define to 1 to see all memory operations. -#define TRACE_MEMORY 0 -// Define to 1 to use in multithreaded environment. +// Define to 1 to use in the multithreaded environment. #define CONCURRENT 0 +// If garbage collection algorithm for cyclic garbage to be used. +#define USE_GC 1 +// Define to 1 to print all memory operations. +#define TRACE_MEMORY 0 +// Trace garbage collection phases. +#define TRACE_GC_PHASES 0 -ContainerHeader ObjHeader::theStaticObjectsContainer = { CONTAINER_TAG_NOCOUNT }; +ContainerHeader ObjHeader::theStaticObjectsContainer = { + CONTAINER_TAG_NOCOUNT | CONTAINER_TAG_INCREMENT +}; namespace { -// Current number of allocated containers. -int allocCount = 0; -#if TRACE_MEMORY -// List of all global objects addresses. -std::vector* globalObjects = nullptr; -// Set of all containers. -std::set* containers = nullptr; +#if USE_GC +// Collection threshold default (collect after having so many elements in the +// release candidates set). +constexpr size_t kGcThreshold = 10000; #endif +#if TRACE_MEMORY || USE_GC +typedef std::unordered_set ContainerHeaderSet; +typedef std::vector ContainerHeaderList; +typedef std::vector KRefPtrList; +#endif + +struct MemoryState { + // Current number of allocated containers. + int allocCount = 0; + +#if TRACE_MEMORY + // List of all global objects addresses. + KRefPtrList* globalObjects; + // Set of all containers. + ContainerHeaderSet* containers; +#endif + +#if USE_GC + // Set of references to release. + ContainerHeaderSet* toFree; + // How many GC suspend requests happened. + int gcSuspendCount; + // How many candidate elements in toFree shall trigger collection. + size_t gcThreshold; + // If collection is in progress. + bool gcInProgress; +#endif +}; + +MemoryState* memoryState = nullptr; + +#if USE_GC +bool isPermanent(const ContainerHeader* header) { + return (header->ref_count_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_NOCOUNT; +} + +// Must be vector or map 'container -> number', to keep reference counters correct. +ContainerHeaderList collectMutableReferred(ContainerHeader* header) { + ContainerHeaderList result; + ObjHeader* obj = reinterpret_cast(header + 1); + const TypeInfo* typeInfo = obj->type_info(); + // TODO: generalize iteration over all references. + // TODO: this code relies on single object per container assumption. + for (int index = 0; index < typeInfo->objOffsetsCount_; index++) { + ObjHeader** location = reinterpret_cast( + reinterpret_cast(obj + 1) + typeInfo->objOffsets_[index]); + ObjHeader* obj = *location; + if (obj != nullptr && !isPermanent(obj->container())) { + result.push_back(obj->container()); + } + } + if (typeInfo == theArrayTypeInfo) { + ArrayHeader* array = obj->array(); + for (int index = 0; index < array->count_; index++) { + ObjHeader* obj = *ArrayAddressOfElementAt(array, index); + if (obj != nullptr && !isPermanent(obj->container())) { + result.push_back(obj->container()); + } + } + } + return result; +} + +void dumpWorker(const char* prefix, ContainerHeader* header, ContainerHeaderSet* seen) { + fprintf(stderr, "%s: %p (%08x): %d refs %s\n", + prefix, + header, header->ref_count_, header->ref_count_ >> CONTAINER_TAG_SHIFT, + (header->ref_count_ & CONTAINER_TAG_SEEN) != 0 ? "X" : "-"); + seen->insert(header); + auto children = collectMutableReferred(header); + for (auto child : children) { + if (seen->count(child) == 0) { + dumpWorker(prefix, child, seen); + } + } +} + +void dumpReachable(const char* prefix, const ContainerHeaderSet* roots) { + ContainerHeaderSet seen; + for (auto container : *roots) { + dumpWorker(prefix, container, &seen); + } +} + +void phase1(ContainerHeader* header) { + if ((header->ref_count_ & CONTAINER_TAG_SEEN) != 0) + return; + header->ref_count_ |= CONTAINER_TAG_SEEN; + auto containers = collectMutableReferred(header); + for (auto container : containers) { + container->ref_count_ -= CONTAINER_TAG_INCREMENT; + phase1(container); + } +} + +void phase2(ContainerHeader* header, ContainerHeaderSet* rootset) { + if ((header->ref_count_ & CONTAINER_TAG_SEEN) == 0) + return; + if ((header->ref_count_ >> CONTAINER_TAG_SHIFT) != 0) + rootset->insert(header); + header->ref_count_ &= ~CONTAINER_TAG_SEEN; + auto containers = collectMutableReferred(header); + for (auto container : containers) { + phase2(container, rootset); + } +} + +void phase3(ContainerHeader* header) { + if ((header->ref_count_ & CONTAINER_TAG_SEEN) != 0) { + return; + } + header->ref_count_ |= CONTAINER_TAG_SEEN; + auto containers = collectMutableReferred(header); + for (auto container : containers) { + container->ref_count_ += CONTAINER_TAG_INCREMENT; + phase3(container); + } +} + +void phase4(ContainerHeader* header, ContainerHeaderSet* toRemove) { + auto ref_count = header->ref_count_ >> CONTAINER_TAG_SHIFT; + bool seen = (ref_count > 0 && (header->ref_count_ & CONTAINER_TAG_SEEN) == 0) || + (ref_count == 0 && (header->ref_count_ & CONTAINER_TAG_SEEN) != 0); + if (seen) return; + + // Add to toRemove set. + if (ref_count == 0) + toRemove->insert(header); + + // Update seen bit. + if (ref_count == 0) + header->ref_count_ |= CONTAINER_TAG_SEEN; + else + header->ref_count_ &= ~CONTAINER_TAG_SEEN; + auto containers = collectMutableReferred(header); + for (auto container : containers) { + phase4(container, toRemove); + } +} + +#endif // USE_GC + } // namespace ContainerHeader* AllocContainer(size_t size) { ContainerHeader* result = reinterpret_cast(calloc(1, size)); #if TRACE_MEMORY - printf(">>> alloc %d -> %p\n", (int)size, result); - containers->insert(result); + fprintf(stderr, ">>> alloc %d -> %p\n", (int)size, result); + memoryState->containers->insert(result); #endif // TODO: atomic increment in concurrent case. - allocCount++; + memoryState->allocCount++; return result; } void FreeContainer(ContainerHeader* header) { + RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed"); #if TRACE_MEMORY - printf("<<< free %p\n", header); - containers->erase(header); + fprintf(stderr, "<<< free %p\n", header); + memoryState->containers->erase(header); #endif header->ref_count_ = CONTAINER_TAG_INVALID; - +#if USE_GC + if (memoryState->toFree) + memoryState->toFree->erase(header); +#endif // Now let's clean all object's fields in this container. // TODO: this is gross hack, relying on the fact that we now only alloc // ArenaContainer and ObjectContainer, which both have single element. @@ -69,10 +218,30 @@ void FreeContainer(ContainerHeader* header) { // And release underlying memory. // TODO: atomic decrement in concurrent case. - allocCount--; +#if CONCURRENT + #error "Atomic update of allocCount" +#endif + memoryState->allocCount--; free(header); } +#if USE_GC +void FreeContainerNoRef(ContainerHeader* header) { + RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed"); +#if TRACE_MEMORY + fprintf(stderr, "<<< free %p\n", header); + memoryState->containers->erase(header); +#endif + header->ref_count_ = CONTAINER_TAG_INVALID; +#if USE_GC + if (memoryState->toFree) + memoryState->toFree->erase(header); +#endif + memoryState->allocCount--; + free(header); +} +#endif + ArenaContainer::ArenaContainer(uint32_t size) { ArenaContainerHeader* header = static_cast(AllocContainer(size + sizeof(ArenaContainerHeader))); @@ -92,7 +261,7 @@ void ObjectContainer::Init(const TypeInfo* type_info) { // header->ref_count_ is zero initialized by AllocContainer(). SetMeta(GetPlace(), type_info); #if TRACE_MEMORY - printf("object at %p\n", GetPlace()); + fprintf(stderr, "object at %p\n", GetPlace()); #endif } } @@ -109,7 +278,7 @@ void ArrayContainer::Init(const TypeInfo* type_info, uint32_t elements) { GetPlace()->count_ = elements; SetMeta(GetPlace()->obj(), type_info); #if TRACE_MEMORY - printf("array at %p\n", GetPlace()); + fprintf(stderr, "array at %p\n", GetPlace()); #endif } } @@ -139,16 +308,37 @@ ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, int count) { inline void AddRef(const ObjHeader* object) { #if TRACE_MEMORY - printf("AddRef on %p in %p\n", object, object->container()); + fprintf(stderr, "AddRef on %p in %p\n", object, object->container()); #endif AddRef(object->container()); +#if USE_GC + // TODO: one could remove from toFree set here, as now container is reachable + // from the rootset, so cannot be cycle collection candidate. + // memoryState->toFree->erase(object->container()); +#endif } inline void ReleaseRef(const ObjHeader* object) { #if TRACE_MEMORY - printf("ReleaseRef on %p in %p\n", object, object->container()); + fprintf(stderr, "ReleaseRef on %p in %p\n", object, object->container()); #endif +#if USE_GC + // If object is not a cycle candidate - just return. + if (Release(object->container())) { + return; + } +#if TRACE_MEMORY + fprintf(stderr, "%p is release candidate\n", object->container()); +#endif + if (memoryState->toFree != nullptr) { + memoryState->toFree->insert(object->container()); + if (memoryState->gcSuspendCount == 0 && + memoryState->toFree->size() > memoryState->gcThreshold) + GarbageCollect(); + } +#else // !USE_GC Release(object->container()); +#endif // USE_GC } extern "C" { @@ -162,36 +352,51 @@ void InitMemory() { == offsetof(ObjHeader , container_offset_negative_), "Layout mismatch"); + RuntimeAssert(memoryState == nullptr, "memory state must be clear"); + memoryState = new MemoryState(); // TODO: initialize heap here. - allocCount = 0; + memoryState->allocCount = 0; #if TRACE_MEMORY - globalObjects = new std::vector(); - containers = new std::set(); + memoryState->globalObjects = new KRefPtrList(); + memoryState->containers = new ContainerHeaderSet(); +#endif +#if USE_GC +#if CONCURRENT + #error "Concurrent GC is not yet implemented" +#endif + memoryState->toFree = new ContainerHeaderSet(); + memoryState->gcInProgress = false; + memoryState->gcThreshold = kGcThreshold; + memoryState->gcSuspendCount = 0; #endif } void DeinitMemory() { #if TRACE_MEMORY // Free all global objects, to ensure no memory leaks happens. - for (auto location: *globalObjects) { - printf("Release global in *%p: %p\n", location, *location); + for (auto location: *memoryState->globalObjects) { + fprintf(stderr, "Release global in *%p: %p\n", location, *location); UpdateGlobalRef(location, nullptr); } - delete globalObjects; - globalObjects = nullptr; + delete memoryState->globalObjects; + memoryState->globalObjects = nullptr; #endif - if (allocCount > 0) { +#if USE_GC + GarbageCollect(); +#endif // USE_GC + + if (memoryState->allocCount > 0) { #if TRACE_MEMORY - // TODO: move out of TRACE_MEMORY, once exceptions free memory. - printf("*** Memory leaks, leaked %d containers ***\n", allocCount); - for (auto container: *containers) { - printf("Unfreed container %p, count = %d\n", container, container->ref_count_); - } - delete containers; - containers = nullptr; + fprintf(stderr, "*** Memory leaks, leaked %d containers ***\n", memoryState->allocCount); + dumpReachable("", memoryState->containers); + delete memoryState->containers; + memoryState->containers = nullptr; #endif } + + delete memoryState; + memoryState = nullptr; } // Now we ignore all placement hints and always allocate heap space for new object. @@ -242,7 +447,7 @@ OBJ_GETTER(InitInstance, // TODO: locking or smth lock-free in MT case? #endif #if TRACE_MEMORY - globalObjects->push_back(location); + memoryState->globalObjects->push_back(location); #endif RETURN_OBJ_RESULT(); } catch (...) { @@ -254,7 +459,7 @@ OBJ_GETTER(InitInstance, void SetLocalRef(ObjHeader** location, const ObjHeader* object) { #if TRACE_MEMORY - printf("SetLocalRef *%p: %p\n", location, object); + fprintf(stderr, "SetLocalRef *%p: %p\n", location, object); #endif *const_cast(location) = object; if (object != nullptr) { @@ -264,7 +469,7 @@ void SetLocalRef(ObjHeader** location, const ObjHeader* object) { void SetGlobalRef(ObjHeader** location, const ObjHeader* object) { #if TRACE_MEMORY - printf("SetGlobalRef *%p: %p\n", location, object); + fprintf(stderr, "SetGlobalRef *%p: %p\n", location, object); #endif *const_cast(location) = object; if (object != nullptr) { @@ -278,16 +483,16 @@ void SetGlobalRef(ObjHeader** location, const ObjHeader* object) { void UpdateLocalRef(ObjHeader** location, const ObjHeader* object) { ObjHeader* old = *location; #if TRACE_MEMORY - printf("UpdateLocalRef *%p: %p -> %p\n", location, old, object); + fprintf(stderr, "UpdateLocalRef *%p: %p -> %p\n", location, old, object); #endif if (old != object) { + if (object != nullptr) { + AddRef(object); + } *const_cast(location) = object; if (old > reinterpret_cast(1)) { ReleaseRef(old); } - if (object != nullptr) { - AddRef(object); - } } } @@ -295,7 +500,7 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) { #if CONCURRENT ObjHeader* old = *location; #if TRACE_MEMORY - printf("UpdateGlobalRef *%p: %p -> %p\n", location, old, object); + fprintf(stderr, "UpdateGlobalRef *%p: %p -> %p\n", location, old, object); #endif if (old != object) { if (object != nullptr) { @@ -320,7 +525,7 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) { void ReleaseLocalRefs(ObjHeader** start, int count) { #if TRACE_MEMORY - printf("ReleaseLocalRefs %p .. %p\n", start, start + count); + fprintf(stderr, "ReleaseLocalRefs %p .. %p\n", start, start + count); #endif ObjHeader** current = start; while (count-- > 0) { @@ -336,7 +541,7 @@ void ReleaseLocalRefs(ObjHeader** start, int count) { void ReleaseGlobalRefs(ObjHeader** start, int count) { #if TRACE_MEMORY - printf("ReleaseGlobalRefs %p .. %p\n", start, start + count); + fprintf(stderr, "ReleaseGlobalRefs %p .. %p\n", start, start + count); #endif #if CONCURRENT ObjHeader** current = start; @@ -364,4 +569,126 @@ void ReleaseGlobalRefs(ObjHeader** start, int count) { #endif } +#if USE_GC +void GarbageCollect() { + RuntimeAssert(memoryState->toFree != nullptr, "GC must not be stopped"); + RuntimeAssert(!memoryState->gcInProgress, "Recursive GC is disallowed"); + memoryState->gcInProgress = true; + // Traverse inner pointers in the closure of release candidates, and + // temporary decrement refs on them. Set CONTAINER_TAG_SEEN while traversing. +#if TRACE_GC_PHASES + dumpReachable("P0", memoryState->toFree); +#endif + for (auto container : *memoryState->toFree) { + phase1(container); + } +#if TRACE_GC_PHASES + dumpReachable("P1", memoryState->toFree); +#endif + + // Collect rootset from containers with non-zero reference counter. Those must + // be referenced from outside of newly released object graph. + // Clear CONTAINER_TAG_SEEN while traversing. + ContainerHeaderSet rootset; + for (auto container : *memoryState->toFree) { + phase2(container, &rootset); + } +#if TRACE_GC_PHASES + dumpReachable("P2", memoryState->toFree); +#endif + + // Increment references for all elements reachable from the rootset. + // Set CONTAINER_TAG_SEEN while traversing. + for (auto container : rootset) { +#if TRACE_MEMORY + fprintf(stderr, "rootset %p\n", container); +#endif + phase3(container); + } +#if TRACE_GC_PHASES + dumpReachable("P3", memoryState->toFree); +#endif + + // Traverse all elements, and collect those not having CONTAINER_TAG_SEEN and zero RC. + // Clear CONTAINER_TAG_SEEN while traversing on live elements, set in on dead elements. + ContainerHeaderSet toRemove; + for (auto container : *memoryState->toFree) { + phase4(container, &toRemove); + } +#if TRACE_GC_PHASES + dumpReachable("P4", memoryState->toFree); +#endif + + // Clear cycle candidates list. + memoryState->toFree->clear(); + + for (auto header : toRemove) { + RuntimeAssert((header->ref_count_ & CONTAINER_TAG_SEEN) != 0, "Must be not seen"); + FreeContainerNoRef(header); + } + + memoryState->gcInProgress = false; +} + +#endif // USE_GC + +void Kotlin_konan_internal_GC_collect(KRef) { +#if USE_GC + GarbageCollect(); +#endif +} + +void Kotlin_konan_internal_GC_suspend(KRef) { +#if USE_GC + memoryState->gcSuspendCount++; +#endif +} + +void Kotlin_konan_internal_GC_resume(KRef) { +#if USE_GC + if (memoryState->gcSuspendCount > 0) { + memoryState->gcSuspendCount--; + if (memoryState->toFree != nullptr && + memoryState->toFree->size() >= memoryState->gcThreshold) { + GarbageCollect(); + } + } +#endif +} + +void Kotlin_konan_internal_GC_stop(KRef) { +#if USE_GC + if (memoryState->toFree != nullptr) { + GarbageCollect(); + delete memoryState->toFree; + memoryState->toFree = nullptr; + } +#endif +} + +void Kotlin_konan_internal_GC_start(KRef) { +#if USE_GC + if (memoryState->toFree == nullptr) { + memoryState->toFree = new ContainerHeaderSet(); + } +#endif +} + +void Kotlin_konan_internal_GC_setThreshold(KRef, KInt value) { +#if USE_GC + if (value > 0) { + memoryState->gcThreshold = value; + } +#endif +} + +KInt Kotlin_konan_internal_GC_getThreshold(KRef) { +#if USE_GC + return memoryState->gcThreshold; +#else + return -1; +#endif +} + + } // extern "C" diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 62d8abcb629..6748e893e2d 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -26,10 +26,14 @@ typedef enum { CONTAINER_TAG_SHARED = 2, // Container is no longer valid. CONTAINER_TAG_INVALID = 3, + // Container was seen during GC. + CONTAINER_TAG_SEEN = 4, + // Shift to get actual counter.. + CONTAINER_TAG_SHIFT = 3, // Actual value to increment/decrement conatiner by. Tag is in lower bits. - CONTAINER_TAG_INCREMENT = 1 << 2, - // Mask for container type. - CONTAINER_TAG_MASK = (CONTAINER_TAG_INCREMENT - 1) + CONTAINER_TAG_INCREMENT = 1 << CONTAINER_TAG_SHIFT, + // Mask for container type, disregard seen bit. + CONTAINER_TAG_MASK = ((CONTAINER_TAG_INCREMENT >> 1) - 1) } ContainerTag; // Could be made 64-bit for large memory configs. @@ -127,8 +131,8 @@ inline uint32_t ArrayDataSizeBytes(const ArrayHeader* obj) { void FreeContainer(ContainerHeader* header); -// Those two operations are implemented by translator when storing references -// to objects. +// TODO: those two operations can be implemented by translator when storing +// reference to an object. inline void AddRef(ContainerHeader* header) { // Looking at container type we may want to skip AddRef() totally // (non-escaping stack objects, constant objects). @@ -144,18 +148,25 @@ inline void AddRef(ContainerHeader* header) { case CONTAINER_TAG_INVALID: RuntimeAssert(false, "trying to addref invalid container"); break; + default: + RuntimeAssert(false, "unknown container type"); + break; } } -inline void Release(ContainerHeader* header) { +// Release returns 'true' iff container cannot be part of cycle (either NOCOUNT +// object or container was fully released and will be collected). +inline bool Release(ContainerHeader* header) { switch (header->ref_count_ & CONTAINER_TAG_MASK) { case CONTAINER_TAG_NORMAL: if ((header->ref_count_ -= CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_NORMAL) { FreeContainer(header); + return true; } break; case CONTAINER_TAG_NOCOUNT: - break; + // NOCOUNT containers aren't loop candidate. + return true; // Note that shared containers have potentially subtle race, if object holds a // reference to another object, stored in shorter living container. In this // case there's unlikely, but possible case, where one mutator takes reference, @@ -173,12 +184,18 @@ inline void Release(ContainerHeader* header) { if (__sync_sub_and_fetch( &header->ref_count_, CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_SHARED) { FreeContainer(header); + return true; } break; case CONTAINER_TAG_INVALID: RuntimeAssert(false, "trying to release invalid container"); break; + default: + RuntimeAssert(false, "unknown container type"); + break; } + // Object with non-zero counter after release are loop candidates. + return false; } // Class representing arbitrary placement container. @@ -346,6 +363,8 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTH // Optimization: release all references in range. void ReleaseLocalRefs(ObjHeader** start, int count) RUNTIME_NOTHROW; void ReleaseGlobalRefs(ObjHeader** start, int count) RUNTIME_NOTHROW; +// Collect garbage, which cannot be found by reference counting (cycles). +void GarbageCollect() RUNTIME_NOTHROW; #ifdef __cplusplus } diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index 2c92a7f676e..d3e9b49c479 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -80,6 +80,7 @@ KInt Kotlin_Byte_unaryPlus (KByte a ) { return +a; } KInt Kotlin_Byte_unaryMinus (KByte a ) { return -a; } KByte Kotlin_Byte_toByte (KByte a ) { return a; } +KChar Kotlin_Byte_toChar (KByte a ) { return a; } KShort Kotlin_Byte_toShort (KByte a ) { return a; } KInt Kotlin_Byte_toInt (KByte a ) { return a; } KLong Kotlin_Byte_toLong (KByte a ) { return a; } @@ -136,6 +137,7 @@ KInt Kotlin_Short_unaryPlus (KShort a ) { return +a; } KInt Kotlin_Short_unaryMinus (KShort a ) { return -a; } KByte Kotlin_Short_toByte (KShort a ) { return a; } +KChar Kotlin_Short_toChar (KShort a ) { return a; } KShort Kotlin_Short_toShort (KShort a ) { return a; } KInt Kotlin_Short_toInt (KShort a ) { return a; } KLong Kotlin_Short_toLong (KShort a ) { return a; } @@ -268,6 +270,7 @@ KLong Kotlin_Long_ushr_Int (KLong a, KInt b) { } KByte Kotlin_Long_toByte (KLong a ) { return a; } +KChar Kotlin_Long_toChar (KLong a ) { return a; } KShort Kotlin_Long_toShort (KLong a ) { return a; } KInt Kotlin_Long_toInt (KLong a ) { return a; } KLong Kotlin_Long_toLong (KLong a ) { return a; } diff --git a/runtime/src/main/cpp/TypeInfo.cpp b/runtime/src/main/cpp/TypeInfo.cpp index b88aa05dbdc..8524f532266 100644 --- a/runtime/src/main/cpp/TypeInfo.cpp +++ b/runtime/src/main/cpp/TypeInfo.cpp @@ -3,7 +3,7 @@ // If one shall use binary search when looking up methods and fields. // TODO: maybe select strategy basing on number of elements. -#define USE_BINARY_SEARCH 0 +#define USE_BINARY_SEARCH 1 extern "C" { #if USE_BINARY_SEARCH diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index 6593b65b914..e34a6c0b225 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -3,6 +3,7 @@ #include +#include "Common.h" #include "Names.h" // An element of sorted by hash in-place array representing methods. @@ -33,24 +34,30 @@ struct TypeInfo { const TypeInfo* const* implementedInterfaces_; int32_t implementedInterfacesCount_; // Null for abstract classes and interfaces. - // TODO: place vtable at the end of TypeInfo to eliminate the indirection. - void* const* vtable_; - // Null for abstract classes and interfaces. const MethodTableRecord* openMethods_; uint32_t openMethodsCount_; const FieldTableRecord* fields_; // Is negative to mark an interface. int32_t fieldsCount_; + + // vtable starts just after declared contents of the TypeInfo: + // void* const vtable_[]; }; #ifdef __cplusplus extern "C" { #endif // Find offset of given hash in table. -int LookupFieldOffset(const TypeInfo* type_info, FieldNameHash hash); +// Note, that we use attribute const, which assumes function doesn't +// dereference global memory, while this function does. However, it seems +// to be safe, as actual result of this computation depends only on 'type_info' +// and 'hash' numeric values and doesn't really depends on global memory state +// (as TypeInfo is compile time constant and type info pointers are stable). +int LookupFieldOffset(const TypeInfo* type_info, FieldNameHash hash) RUNTIME_CONST; // Find open method by its hash. Other methods are resolved in compile-time. -void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature); +// See comment in LookupFieldOffset(). +void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST; #ifdef __cplusplus } // extern "C" diff --git a/runtime/src/main/cpp/Types.h b/runtime/src/main/cpp/Types.h index 4ee1a02aa1f..771655ad3d2 100644 --- a/runtime/src/main/cpp/Types.h +++ b/runtime/src/main/cpp/Types.h @@ -1,6 +1,7 @@ #ifndef RUNTIME_TYPES_H #define RUNTIME_TYPES_H +#include "Common.h" #include "Memory.h" #include "TypeInfo.h" @@ -36,9 +37,9 @@ extern const TypeInfo* theBooleanArrayTypeInfo; extern const TypeInfo* theStringTypeInfo; extern const TypeInfo* theThrowableTypeInfo; -KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info); +KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE; void CheckCast(const ObjHeader* obj, const TypeInfo* type_info); -KBoolean IsArray(KConstRef obj); +KBoolean IsArray(KConstRef obj) RUNTIME_PURE; typedef void (*Initializer)(); struct InitNode { diff --git a/runtime/src/main/kotlin/konan/Annotations.kt b/runtime/src/main/kotlin/konan/Annotations.kt index 4ff0173ecb6..dbd0712ed4f 100644 --- a/runtime/src/main/kotlin/konan/Annotations.kt +++ b/runtime/src/main/kotlin/konan/Annotations.kt @@ -26,21 +26,11 @@ public annotation class Used // Following annotations can be used to mark functions that need to be fixed, // once certain language feature is implemented. -/** - * Need to be fixed because of boxing. - */ -public annotation class FixmeBoxing - /** * Need to be fixed because of inner classes. */ public annotation class FixmeInner -/** - * Need to be fixed because of lambdas. - */ -public annotation class FixmeLambda - /** * Need to be fixed. */ diff --git a/runtime/src/main/kotlin/konan/internal/Boxing.kt b/runtime/src/main/kotlin/konan/internal/Boxing.kt index 59534ea2fc6..7dc4f6fbdc3 100644 --- a/runtime/src/main/kotlin/konan/internal/Boxing.kt +++ b/runtime/src/main/kotlin/konan/internal/Boxing.kt @@ -20,6 +20,76 @@ class BooleanBox(val value: Boolean) : Comparable { fun boxBoolean(value: Boolean) = BooleanBox(value) +class CharBox(val value: Char) : Comparable { + override fun equals(other: Any?): Boolean { + if (other !is CharBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Char): Int = value.compareTo(other) +} + +fun boxChar(value: Char) = CharBox(value) + +class ByteBox(val value: Byte) : Number(), Comparable { + override fun equals(other: Any?): Boolean { + if (other !is ByteBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Byte): Int = value.compareTo(other) + + override fun toByte() = value.toByte() + override fun toChar() = value.toChar() + override fun toShort() = value.toShort() + override fun toInt() = value.toInt() + override fun toLong() = value.toLong() + override fun toFloat() = value.toFloat() + override fun toDouble() = value.toDouble() +} + +fun boxByte(value: Byte) = ByteBox(value) + +class ShortBox(val value: Short) : Number(), Comparable { + override fun equals(other: Any?): Boolean { + if (other !is ShortBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Short): Int = value.compareTo(other) + + override fun toByte() = value.toByte() + override fun toChar() = value.toChar() + override fun toShort() = value.toShort() + override fun toInt() = value.toInt() + override fun toLong() = value.toLong() + override fun toFloat() = value.toFloat() + override fun toDouble() = value.toDouble() +} + +fun boxShort(value: Short) = ShortBox(value) + class IntBox(val value: Int) : Number(), Comparable { override fun equals(other: Any?): Boolean { if (other !is IntBox) { @@ -46,4 +116,80 @@ class IntBox(val value: Int) : Number(), Comparable { fun boxInt(value: Int) = IntBox(value) -// TODO: support other boxes. \ No newline at end of file +class LongBox(val value: Long) : Number(), Comparable { + override fun equals(other: Any?): Boolean { + if (other !is LongBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Long): Int = value.compareTo(other) + + override fun toByte() = value.toByte() + override fun toChar() = value.toChar() + override fun toShort() = value.toShort() + override fun toInt() = value.toInt() + override fun toLong() = value.toLong() + override fun toFloat() = value.toFloat() + override fun toDouble() = value.toDouble() +} + +fun boxLong(value: Long) = LongBox(value) + +class FloatBox(val value: Float) : Number(), Comparable { + override fun equals(other: Any?): Boolean { + if (other !is FloatBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Float): Int = value.compareTo(other) + + override fun toByte() = value.toByte() + override fun toChar() = value.toChar() + override fun toShort() = value.toShort() + override fun toInt() = value.toInt() + override fun toLong() = value.toLong() + override fun toFloat() = value.toFloat() + override fun toDouble() = value.toDouble() +} + +fun boxFloat(value: Float) = FloatBox(value) + +class DoubleBox(val value: Double) : Number(), Comparable { + override fun equals(other: Any?): Boolean { + if (other !is DoubleBox) { + return false + } + + return this.value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + override fun compareTo(other: Double): Int = value.compareTo(other) + + override fun toByte() = value.toByte() + override fun toChar() = value.toChar() + override fun toShort() = value.toShort() + override fun toInt() = value.toInt() + override fun toLong() = value.toLong() + override fun toFloat() = value.toFloat() + override fun toDouble() = value.toDouble() +} + +fun boxDouble(value: Double) = DoubleBox(value) diff --git a/runtime/src/main/kotlin/konan/internal/GC.kt b/runtime/src/main/kotlin/konan/internal/GC.kt new file mode 100644 index 00000000000..e2c775cca5b --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/GC.kt @@ -0,0 +1,52 @@ +package konan.internal + +// Cycle garbage collector interface. +// +// Konan relies upon reference counting for object management, however it could +// not collect cyclical garbage, so we perform periodic garbage collection. +// This may slow down application, so this interface provides control over how +// garbage collector activates and runs. +// Garbage collector can be in one of the following states: +// * running +// * suspended (so cycle candidates are collected, but GC is not performed until resume) +// * stopped (all cyclical garbage is hopelessly lost) +// Immediately after startup GC is in running state. +// Depending on application needs it may select to suspend GC for certain phases of +// its lifetime, and resume it later on, or just completely turn it off, if GC pauses +// are less desirable than cyclical garbage leaks. +object GC { + // To force garbage collection immediately, unless collector is stopped + // with stop() operation. Even if GC is suspended, collect() still triggers collection. + @SymbolName("Kotlin_konan_internal_GC_collect") + external fun collect() + + // Suspend garbage collection. Release candidates are still collected, but + // GC algorithm is not executed. + @SymbolName("Kotlin_konan_internal_GC_suspend") + external fun suspend() + + // Resume garbage collection. Can potentially lead to GC immediately. + @SymbolName("Kotlin_konan_internal_GC_resume") + external fun resume() + + // Stop garbage collection. Cyclical garbage is no longer collected. + @SymbolName("Kotlin_konan_internal_GC_stop") + external fun stop() + + // Start garbage collection. Cyclical garbage produced while GC was stopped + // cannot be reclaimed, but all new garbage is collected. + @SymbolName("Kotlin_konan_internal_GC_start") + external fun start() + + // GC threshold, controlling how frequenly GC is activated, and how much time GC + // takes. Bigger values lead to longer GC pauses, but less GCs. + var threshold: Int + get() = getThreshold() + set(value) = setThreshold(value) + + @SymbolName("Kotlin_konan_internal_GC_getThreshold") + private external fun getThreshold(): Int + + @SymbolName("Kotlin_konan_internal_GC_setThreshold") + private external fun setThreshold(value: Int) +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/Array.kt b/runtime/src/main/kotlin/kotlin/Array.kt index 0e85653c63e..6bb2387e3fe 100644 --- a/runtime/src/main/kotlin/kotlin/Array.kt +++ b/runtime/src/main/kotlin/kotlin/Array.kt @@ -3,9 +3,15 @@ package kotlin // TODO: remove that, as RTTI shall be per instantiation. @ExportTypeInfo("theArrayTypeInfo") public final class Array : Cloneable { - // TODO: actual constructor has initializer parameter, implement it once lambdas are implemented. // Constructors are handled with compiler magic. - public constructor(@Suppress("UNUSED_PARAMETER") size: Int) {} + public constructor(size: Int, init: (Int) -> T) { + var index = 0 + while (index < size) { + this[index] = init(index) + index++ + } + } + internal constructor(@Suppress("UNUSED_PARAMETER") size: Int) {} public val size: Int get() = getArrayLength() diff --git a/runtime/src/main/kotlin/kotlin/Arrays.kt b/runtime/src/main/kotlin/kotlin/Arrays.kt index 5134c411f75..78e5f6c0bab 100644 --- a/runtime/src/main/kotlin/kotlin/Arrays.kt +++ b/runtime/src/main/kotlin/kotlin/Arrays.kt @@ -507,3 +507,89 @@ public val Array.indices: IntRange */ public val Array.lastIndex: Int get() = size - 1 + +/** + * Applies the given [transform] function to each element of the original array + * and appends the results to the given [destination]. + */ +public inline fun > Array.mapTo(destination: C, transform: (T) -> R): C { + for (item in this) + destination.add(transform(item)) + return destination +} + +/** + * Returns a list containing the results of applying the given [transform] function + * to each element in the original array. + */ +public inline fun Array.map(transform: (T) -> R): List { + return mapTo(ArrayList(size), transform) +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Int { + var sum: Int = 0 + for (element in this) { + sum += element + } + return sum +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Int { + var sum: Int = 0 + for (element in this) { + sum += element + } + return sum +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Int { + var sum: Int = 0 + for (element in this) { + sum += element + } + return sum +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Long { + var sum: Long = 0L + for (element in this) { + sum += element + } + return sum +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Float { + var sum: Float = 0.0f + for (element in this) { + sum += element + } + return sum +} + +/** + * Returns the sum of all elements in the array. + */ +public fun Array.sum(): Double { + var sum: Double = 0.0 + for (element in this) { + sum += element + } + return sum +} + + diff --git a/runtime/src/main/kotlin/kotlin/Primitives.kt b/runtime/src/main/kotlin/kotlin/Primitives.kt index c6021b88487..439470934ec 100644 --- a/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -1091,8 +1091,7 @@ public final class Float : Number(), Comparable { @SymbolName("Kotlin_Float_toByte") external public override fun toByte(): Byte - @SymbolName("Kotlin_Float_toChar") - external public override fun toChar(): Char + public override fun toChar(): Char = this.toInt().toChar() @SymbolName("Kotlin_Float_toShort") external public override fun toShort(): Short @SymbolName("Kotlin_Float_toInt") @@ -1302,8 +1301,7 @@ public final class Double : Number(), Comparable { @SymbolName("Kotlin_Double_toByte") external public override fun toByte(): Byte - @SymbolName("Kotlin_Double_toChar") - external public override fun toChar(): Char + public override fun toChar(): Char = this.toInt().toChar() @SymbolName("Kotlin_Double_toShort") external public override fun toShort(): Short @SymbolName("Kotlin_Double_toInt") diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt index 4ce36677c97..b2faaa21d2c 100644 --- a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt @@ -1,13 +1,13 @@ package kotlin.collections /** - * Returns an array of objects of the given type with the given [size], initialized with **lateinit** _uninitialized_ values. + * Returns an array of objects of the given type with the given [size], initialized with _uninitialized_ values. * Attempts to read _uninitialized_ values from this array work in implementation-dependent manner, * either throwing exception or returning some kind of implementation-specific default value. */ -fun arrayOfUninitializedElements(size: Int): Array { - // return (if (size == 0) emptyArray else Array(size)) as Array - return Array(size) +internal fun arrayOfUninitializedElements(size: Int): Array { + // TODO: special case for size == 0? + return Array(size) } /** @@ -16,7 +16,7 @@ fun arrayOfUninitializedElements(size: Int): Array { * either throwing exception or returning some kind of implementation-specific default value. */ fun Array.copyOfUninitializedElements(newSize: Int): Array { - val result = Array(newSize) + val result = arrayOfUninitializedElements(newSize) this.copyRangeTo(result, 0, if (newSize > this.size) this.size else newSize, 0) return result } @@ -33,7 +33,7 @@ fun IntArray.copyOfUninitializedElements(newSize: Int): IntArray { * Attempts to read _uninitialized_ value work in implementation-dependent manner, * either throwing exception or returning some kind of implementation-specific default value. */ -fun Array.resetAt(index: Int) { +internal fun Array.resetAt(index: Int) { (@Suppress("UNCHECKED_CAST")(this as Array))[index] = null } @@ -50,11 +50,11 @@ external private fun fillImpl(array: IntArray, fromIndex: Int, toIndex: Int, val * Attempts to read _uninitialized_ values work in implementation-dependent manner, * either throwing exception or returning some kind of implementation-specific default value. */ -fun Array.resetRange(fromIndex: Int, toIndex: Int) { +internal fun Array.resetRange(fromIndex: Int, toIndex: Int) { fillImpl(@Suppress("UNCHECKED_CAST") (this as Array), fromIndex, toIndex, null) } -fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) { +internal fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) { fillImpl(this, fromIndex, toIndex, value) } diff --git a/runtime/src/main/kotlin/kotlin/collections/Maps.kt b/runtime/src/main/kotlin/kotlin/collections/Maps.kt index 53a521efb7a..20272206cc3 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Maps.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Maps.kt @@ -59,9 +59,8 @@ public inline fun mapOf(): Map = emptyMap() * @sample samples.collections.Maps.Instantiation.mutableMapFromPairs * @sample samples.collections.Maps.Instantiation.emptyMutableMap */ -@Fixme -public fun mutableMapOf(vararg pairs: Pair): MutableMap = hashMapOf(*pairs) -// = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } +public fun mutableMapOf(vararg pairs: Pair): MutableMap + = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } /** * Returns a new [HashMap] with the specified contents, given as a list of pairs @@ -69,13 +68,8 @@ public fun mutableMapOf(vararg pairs: Pair): MutableMap = has * * @sample samples.collections.Maps.Instantiation.hashMapFromPairs */ -public fun hashMapOf(vararg pairs: Pair): HashMap { -// = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } - val result = HashMap(mapCapacity(pairs.size)) - for (pair in pairs) - result.put(pair.first, pair.second) - return result -} +public fun hashMapOf(vararg pairs: Pair): HashMap + = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } /** * Returns a new [HashMap] with the specified contents, given as a list of pairs @@ -649,11 +643,9 @@ public inline fun Map.mapNotNull(transform: (Map.Entry * Applies the given [transform] function to each entry in the original map * and appends only the non-null results to the given [destination]. */ -@FixmeLambda public inline fun > Map.mapNotNullTo(destination: C, transform: (Map.Entry) -> R?): C { - TODO() - //forEach { element -> transform(element)?.let { destination.add(it) } } - //return destination + forEach { element -> transform(element)?.let { destination.add(it) } } + return destination } /** diff --git a/runtime/src/main/kotlin/kotlin/collections/MutableCollections.kt b/runtime/src/main/kotlin/kotlin/collections/MutableCollections.kt index 9ec6fbe9f18..1713e0e769b 100644 --- a/runtime/src/main/kotlin/kotlin/collections/MutableCollections.kt +++ b/runtime/src/main/kotlin/kotlin/collections/MutableCollections.kt @@ -149,20 +149,16 @@ public fun MutableIterable.removeAll(predicate: (T) -> Boolean): Boolean */ public fun MutableIterable.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false) -// TODO: due to boxing problems, cannot use Boolean type. -@FixmeBoxing private fun MutableIterable.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean { - TODO() - /* var result = false with (iterator()) { while (hasNext()) - if (predicate(next()).toString() == predicateResultToRemove) { + if (predicate(next()) == predicateResultToRemove) { remove() result = true } } - return result */ + return result } /**