From d86b387f9c39da2e668da59e2632a8fbe279fef8 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 17 Nov 2016 16:05:05 +0300 Subject: [PATCH] CODEGEN: Implemented support for "instanceOf", operations "Boolean" type implemented, operator NOT_INSTANCEOF implemented for code --------8<-------- > cat ../backend.native/tests/codegen/basics/check_type.kt interface I class A() : I {} class B() {} //-----------------------------------------------------------------------------// fun isTypeOf(a: Any) : Boolean { return a is A } fun check_type(): Boolean { val a = A() return isTypeOf(a) } //-----------------------------------------------------------------------------// fun isNotTypeOf(a: Any) : Boolean { return a !is A } fun check_not_type(): Boolean { val b = B() return isNotTypeOf(b) } //-----------------------------------------------------------------------------// fun isTypeOfInterface(a: Any) : Boolean { return a is I } fun check_interface(): Boolean { val a = A() return isTypeOfInterface(a) } --------8<-------- translator generates: --------8<-------- > llvm-dis-mp-3.8 ../backend.native/tests/codegen/basics/check_type.kt.bc -o - ; ModuleID = '../backend.native/tests/codegen/basics/check_type.kt.bc' target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" ... define i1 @"kfun:isTypeOf(Any)"(i8*) { entry: %a = alloca i8* store i8* %0, i8** %a %tmp_1 = load i8*, i8** %a %tmp_2 = bitcast i8* %tmp_1 to %struct.ObjHeader* %tmp_3 = call i8 @IsInstance(%struct.ObjHeader* %tmp_2, %struct.TypeInfo* @"ktype:A") %tmp_0 = trunc i8 %tmp_3 to i1 ret i1 %tmp_0 } ... define i1 @"kfun:isNotTypeOf(Any)"(i8*) { entry: %a = alloca i8* store i8* %0, i8** %a %tmp_2 = load i8*, i8** %a %tmp_3 = bitcast i8* %tmp_2 to %struct.ObjHeader* %tmp_4 = call i8 @IsInstance(%struct.ObjHeader* %tmp_3, %struct.TypeInfo* @"ktype:A") %tmp_1 = trunc i8 %tmp_4 to i1 %tmp_0 = xor i1 %tmp_1, true ret i1 %tmp_0 } ... define i1 @"kfun:isTypeOfInterface(Any)"(i8*) { entry: %a = alloca i8* store i8* %0, i8** %a %tmp_1 = load i8*, i8** %a %tmp_2 = bitcast i8* %tmp_1 to %struct.ObjHeader* %tmp_3 = call i8 @IsInstance(%struct.ObjHeader* %tmp_2, %struct.TypeInfo* @"ktype:I") %tmp_0 = trunc i8 %tmp_3 to i1 ret i1 %tmp_0 } --------8<-------- --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 43 ++++++++++++++++++- runtime/src/main/cpp/Operator.cpp | 10 ++++- runtime/src/main/kotlin/kotlin/Boolean.kt | 5 +++ 3 files changed, 55 insertions(+), 3 deletions(-) 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 84977862dc4..962c440b943 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 @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.asSimpleType fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose @@ -371,6 +372,21 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid return codegen.store(ret!!, variable) } + //-------------------------------------------------------------------------// + + private fun evaluateTypeOperator(tmpVariableName: String, value: IrTypeOperatorCall): LLVMOpaqueValue { + when (value.operator) { + IrTypeOperator.CAST -> return evaluateCast(tmpVariableName, value) + IrTypeOperator.IMPLICIT_CAST -> TODO() + IrTypeOperator.IMPLICIT_NOTNULL -> TODO() + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> TODO() + IrTypeOperator.SAFE_CAST -> TODO() + IrTypeOperator.INSTANCEOF -> return evaluateInstanceOf(tmpVariableName, value) + IrTypeOperator.NOT_INSTANCEOF -> return evaluateNotInstanceOf(tmpVariableName, value) + } + + } + //-------------------------------------------------------------------------// // table of conversion with llvm for primitive types // to be used in replacement fo primitive.toX() calls with @@ -384,8 +400,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid // float | fptosi fptosi fptosi fptosi x fpext // double | fptosi fptosi fptosi fptosi fptrunc x - private fun evaluateTypeOperator(tmpVariableName: String, value: IrTypeOperatorCall): LLVMOpaqueValue { - logger.log("evaluateTypeOperator : ${ir2string(value)}") + private fun evaluateCast(tmpVariableName: String, value: IrTypeOperatorCall): LLVMOpaqueValue { + logger.log("evaluateCast : ${ir2string(value)}") assert(!KotlinBuiltIns.isPrimitiveType(value.type) && !KotlinBuiltIns.isPrimitiveType(value.argument.type)) val dstDescriptor = TypeUtils.getClassDescriptor(value.type) // Get class descriptor for dst type. @@ -399,6 +415,28 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// + private fun evaluateInstanceOf(tmpVariableName: String, value: IrTypeOperatorCall): LLVMOpaqueValue { + logger.log("evaluateInstanceOf : ${ir2string(value)}") + + val dstDescriptor = TypeUtils.getClassDescriptor(value.typeOperand) // Get class descriptor for dst type. + val dstTypeInfo = codegen.typeInfoValue(dstDescriptor!!) // Get TypeInfo for dst type. + val srcArg = evaluateExpression(codegen.newVar(), value.argument)!! // Evaluate src expression. + val srcObjInfoPtr = codegen.bitcast(kObjHeaderPtr, srcArg, codegen.newVar()) // Cast src to ObjInfoPtr. + val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list. + + val result = codegen.call(context.isInstanceFunction, args, codegen.newVar()) // Check if dst is subclass of src. + return LLVMBuildTrunc(context.llvmBuilder, result!!, kInt1, tmpVariableName)!! // Truncate result to boolean + } + + //-------------------------------------------------------------------------// + + private fun evaluateNotInstanceOf(tmpVariableName: String, value: IrTypeOperatorCall): LLVMOpaqueValue { + val instanceOfResult = evaluateInstanceOf(codegen.newVar(), value) + return LLVMBuildNot(context.llvmBuilder, instanceOfResult, tmpVariableName)!! + } + + //-------------------------------------------------------------------------// + private fun evaluateGetField(value: IrGetField): LLVMOpaqueValue { logger.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null) { @@ -698,6 +736,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private val kObjHeader = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!! private val kObjHeaderPtr = pointerType(kObjHeader)!! private val kTypeInfoPtr = pointerType(kTypeInfo) + private val kInt1 = LLVMInt1Type() private val kInt8Ptr = pointerType(LLVMInt8Type()) private val kInt8PtrPtr = pointerType(kInt8Ptr) diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index 702ac3f151e..0cb8985a635 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -5,11 +5,19 @@ extern "C" { +//--- Boolean -----------------------------------------------------------------// + +KBoolean Kotlin_Boolean_not (KBoolean a ) { return !a; } +KBoolean Kotlin_Boolean_and_Boolean (KBoolean a, KBoolean b) { return a && b; } +KBoolean Kotlin_Boolean_or_Boolean (KBoolean a, KBoolean b) { return a || b; } +KBoolean Kotlin_Boolean_xor_Boolean (KBoolean a, KBoolean b) { return a != b; } +KInt Kotlin_Boolean_compareTo_Boolean(KBoolean a, KBoolean b) { if (a == b) return 0; return (a < b) ? -1 : 1; } + //--- Char --------------------------------------------------------------------// KInt Kotlin_Char_compareTo_Char (KChar a, KChar b) { if (a == b) return 0; return (a < b) ? -1 : 1; } KChar Kotlin_Char_plus_Char (KChar a, KInt b) { return a + b; } -KChar Kotlin_Char_minus_Char (KChar a, KChar b) { return a - b; } +KInt Kotlin_Char_minus_Char (KChar a, KChar b) { return a - b; } KChar Kotlin_Char_minus_Int (KChar a, KInt b) { return a - b; } KChar Kotlin_Char_inc (KChar a ) { return a + 1; } KChar Kotlin_Char_dec (KChar a ) { return a - 1; } diff --git a/runtime/src/main/kotlin/kotlin/Boolean.kt b/runtime/src/main/kotlin/kotlin/Boolean.kt index 61e30e8915b..d6aff94d668 100644 --- a/runtime/src/main/kotlin/kotlin/Boolean.kt +++ b/runtime/src/main/kotlin/kotlin/Boolean.kt @@ -8,23 +8,28 @@ public final class Boolean : Comparable { /** * Returns the inverse of this boolean. */ + @SymbolName("Kotlin_Boolean_not") external public operator fun not(): Boolean /** * Performs a logical `and` operation between this Boolean and the [other] one. */ + @SymbolName("Kotlin_Boolean_and_Boolean") external public infix fun and(other: Boolean): Boolean /** * Performs a logical `or` operation between this Boolean and the [other] one. */ + @SymbolName("Kotlin_Boolean_or_Boolean") external public infix fun or(other: Boolean): Boolean /** * Performs a logical `xor` operation between this Boolean and the [other] one. */ + @SymbolName("Kotlin_Boolean_xor_Boolean") external public infix fun xor(other: Boolean): Boolean + @SymbolName("Kotlin_Boolean_compareTo_Boolean") external public override fun compareTo(other: Boolean): Int // Konan-specific.