From 0cd4512dd5b632c7f1089b84a1d364aca7ba2818 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Fri, 18 Nov 2016 15:33:06 +0300 Subject: [PATCH] CODEGEN: Support for "null" constant implemented for code: --------8<-------- > cat ../backend.native/tests/codegen/basics/null_check.kt //--- Test "eqeq" -------------------------------------------------------------// fun check_eqeq(a: Any?) = a == null fun null_check_eqeq1() : Boolean { return check_eqeq(Any()) } fun null_check_eqeq2() : Boolean { return check_eqeq(null) } //--- Test "eqeqeq" -----------------------------------------------------------// fun check_eqeqeq(a: Any?) = a === null fun null_check_eqeqeq1() : Boolean { return check_eqeqeq(Any()) } fun null_check_eqeqeq2() : Boolean { return check_eqeqeq(null) } --------8<-------- code generator produces, following code: --------8<-------- > llvm-dis-mp-3.8 ../backend.native/tests/codegen/basics/null_check.kt.bc -o - ; ModuleID = '../backend.native/tests/codegen/basics/null_check.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:check_eqeq(Any?)"(i8*) { entry: %a = alloca i8* store i8* %0, i8** %a %tmp_1 = load i8*, i8** %a %tmp_3 = alloca i1 %tmp_4 = icmp eq i8* %tmp_1, null br i1 %tmp_4, label %label_1, label %label_0 label_0: ; preds = %entry %tmp_7 = bitcast i8* %tmp_1 to i8** %tmp_8 = load i8*, i8** %tmp_7 %tmp_9 = bitcast i8* %tmp_8 to %struct.TypeInfo* %tmp_10 = call i8* @LookupOpenMethod(%struct.TypeInfo* %tmp_9, i64 5922905592022884074) %tmp_11 = bitcast i8* %tmp_10 to i1 (i8*, i8*)* %tmp_6 = call i1 %tmp_11(i8* %tmp_1, i8* null) store i1 %tmp_6, i1* %tmp_3 br label %label_2 label_1: ; preds = %entry %tmp_5 = icmp eq i8* %tmp_1, null store i1 %tmp_5, i1* %tmp_3 br label %label_2 label_2: ; preds = %label_1, %label_0 %tmp_0 = load i1, i1* %tmp_3 ret i1 %tmp_0 } define i1 @"kfun:null_check_eqeq1()"() { entry: %tmp_1 = call i8* @AllocInstance(%struct.TypeInfo* @"ktype:kotlin.Any", i32 1) %tmp_11 = call i8* @"kfun:kotlin.Any.()"(i8* %tmp_1) %tmp_0 = call i1 @"kfun:check_eqeq(Any?)"(i8* %tmp_11) ret i1 %tmp_0 } declare i8* @"kfun:kotlin.Any.()"(i8*) define i1 @"kfun:null_check_eqeq2()"() { entry: %tmp_0 = call i1 @"kfun:check_eqeq(Any?)"(i8* null) ret i1 %tmp_0 } define i1 @"kfun:check_eqeqeq(Any?)"(i8*) { entry: %a = alloca i8* store i8* %0, i8** %a %tmp_1 = load i8*, i8** %a %tmp_0 = icmp eq i8* %tmp_1, null ret i1 %tmp_0 } define i1 @"kfun:null_check_eqeqeq1()"() { entry: %tmp_1 = call i8* @AllocInstance(%struct.TypeInfo* @"ktype:kotlin.Any", i32 1) %tmp_11 = call i8* @"kfun:kotlin.Any.()"(i8* %tmp_1) %tmp_0 = call i1 @"kfun:check_eqeqeq(Any?)"(i8* %tmp_11) ret i1 %tmp_0 } define i1 @"kfun:null_check_eqeqeq2()"() { entry: %tmp_0 = call i1 @"kfun:check_eqeqeq(Any?)"(i8* null) ret i1 %tmp_0 } ... --------8<-------- --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 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 e3393a68d14..4b767885513 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 @@ -20,8 +20,9 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.asSimpleType +import org.jetbrains.kotlin.types.typeUtil.isNullableNothing fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose @@ -544,7 +545,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateConst(value: IrConst<*>): LLVMOpaqueValue? { logger.log("evaluateConst : ${ir2string(value)}") when (value.kind) { - IrConstKind.Null -> TODO() // LLVMConstPointerNull + IrConstKind.Null -> return kNullPtr IrConstKind.Boolean -> when (value.value) { true -> return kTrue false -> return kFalse @@ -672,14 +673,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid logger.log("evaluateCall $tmpVariableName origin:$callee") val descriptor = callee.descriptor when (descriptor.name) { - kEqeq -> return evaluateOperatorEqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) - ktEqeqeq -> - return evaluateOperatorEqeqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) - kGt0 -> return codegen.icmpGt(args[0]!!, kImmZero, tmpVariableName) - kGteq0 -> return codegen.icmpGe(args[0]!!, kImmZero, tmpVariableName) - kLt0 -> return codegen.icmpLt(args[0]!!, kImmZero, tmpVariableName) - kLteq0 -> return codegen.icmpLe(args[0]!!, kImmZero, tmpVariableName) - kNot -> return codegen.icmpNe(args[0]!!, kTrue, tmpVariableName) + kEqeq -> return evaluateOperatorEqeq (callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) + ktEqeqeq -> return evaluateOperatorEqeqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) + kGt0 -> return codegen.icmpGt(args[0]!!, kImmZero, tmpVariableName) + kGteq0 -> return codegen.icmpGe(args[0]!!, kImmZero, tmpVariableName) + kLt0 -> return codegen.icmpLt(args[0]!!, kImmZero, tmpVariableName) + kLteq0 -> return codegen.icmpLe(args[0]!!, kImmZero, tmpVariableName) + kNot -> return codegen.icmpNe(args[0]!!, kTrue, tmpVariableName) else -> { TODO(descriptor.name.toString()) } @@ -688,22 +688,59 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// - private fun evaluateOperatorEqeq(callee: IrBinaryPrimitiveImpl, arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, tmpVariableName: String):LLVMOpaqueValue { + private fun getEquals(type: KotlinType, methodName: String) : SimpleFunctionDescriptor { + + val name = Name.identifier(methodName) + val descriptors = type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).filter { + it.valueParameters.size == 1 && KotlinBuiltIns.isAnyOrNullableAny(it.valueParameters[0].type) + } + assert(descriptors.size == 1) + val descriptor = descriptors.first() + return descriptor + } + + //-------------------------------------------------------------------------// + + private fun evaluateOperatorEqeq(callee: IrBinaryPrimitiveImpl, arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, tmpVariableName: String): LLVMOpaqueValue { + val arg0Type = callee.argument0.type - val isFp = KotlinBuiltIns.isDouble(arg0Type) || KotlinBuiltIns.isFloat(arg0Type) + val isFp = KotlinBuiltIns.isDouble(arg0Type) || KotlinBuiltIns.isFloat(arg0Type) val isInt = KotlinBuiltIns.isPrimitiveType(arg0Type) && !isFp - assert(arg0Type == callee.argument1.type) when { isFp -> return codegen.fcmpEq(arg0, arg1, tmpVariableName) isInt -> return codegen.icmpEq(arg0, arg1, tmpVariableName) - else -> { - val functions = arg0Type.memberScope.getContributedFunctions(Name.identifier("equals"), NoLookupLocation.FROM_BACKEND).filter { - it.valueParameters.size == 1 && KotlinBuiltIns.isNullableAny(it.valueParameters[0].type) - } - assert(functions.size == 1) - val descriptor = functions.first() - return evaluateSimpleFunctionCall(tmpVariableName, descriptor, listOf(arg0, arg1))!! - } + else -> return generateEqeqForObjects(callee, arg0, arg1, tmpVariableName) + } + } + + //-------------------------------------------------------------------------// + + private fun generateEqeqForObjects(callee: IrBinaryPrimitiveImpl, arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, tmpVariableName: String): LLVMOpaqueValue { + val arg0Type = callee.argument0.type + val descriptor = getEquals(arg0Type, "equals") // Get descriptor for "arg0.equals". + if (arg0Type.isMarkedNullable) { // If arg0 is nullable. + val bbEq2 = codegen.basicBlock()!! // Block to process "eqeq". + val bbEq3 = codegen.basicBlock()!! // Block to process "eqeqeq". + val bbExit = codegen.basicBlock()!! // Exit block for feather generation. + val result = codegen.alloca(getLLVMType(callee.type), codegen.newVar()) + + val condition = codegen.icmpEq(arg0, kNullPtr, codegen.newVar()) // Compare arg0 with "null". + codegen.condBr(condition, bbEq3, bbEq2) // If (arg0 == null) bbEq3 else bbEq2. + + codegen.positionAtEnd(bbEq3) // Get generation to bbEq3. + val resultIdentityEq = evaluateOperatorEqeqeq(callee, arg0, arg1, codegen.newVar()) + codegen.store(resultIdentityEq, result) // Store "eqeq" result in "result". + codegen.br(bbExit) // + + codegen.positionAtEnd(bbEq2) // Get generation to bbEq2. + val resultEquals = evaluateSimpleFunctionCall(codegen.newVar(), descriptor, listOf(arg0, arg1))!! + codegen.store(resultEquals, result) // Store "eqeqeq" result in "result". + codegen.br(bbExit) // + + codegen.positionAtEnd(bbExit) // Get generation to bbExit. + return codegen.load(result, tmpVariableName) // Load result in tmpVariable. + } else { + return evaluateSimpleFunctionCall(tmpVariableName, descriptor, listOf(arg0, arg1))!! } } @@ -716,7 +753,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val arg0Type = callee.argument0.type val arg1Type = callee.argument1.type - assert (arg0Type == arg1Type) + assert(arg0Type == arg1Type || arg0Type.isNullableNothing() || arg1Type.isNullableNothing()) return when { KotlinBuiltIns.isPrimitiveType(arg0Type) -> TODO() @@ -774,6 +811,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private val kInt1 = LLVMInt1Type() private val kInt8Ptr = pointerType(LLVMInt8Type()) private val kInt8PtrPtr = pointerType(kInt8Ptr) + private val kNullPtr = LLVMConstNull(kInt8Ptr)!! //-------------------------------------------------------------------------//