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.<init>()"(i8* %tmp_1)
  %tmp_0 = call i1 @"kfun:check_eqeq(Any?)"(i8* %tmp_11)
  ret i1 %tmp_0
}

declare i8* @"kfun:kotlin.Any.<init>()"(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.<init>()"(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<--------
This commit is contained in:
Konstantin Anisimov
2016-11-18 15:33:06 +03:00
committed by vvlevchenko
parent df358e8bd4
commit 0cd4512dd5
@@ -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)!!
//-------------------------------------------------------------------------//