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:
committed by
vvlevchenko
parent
df358e8bd4
commit
0cd4512dd5
+60
-22
@@ -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)!!
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
|
||||
Reference in New Issue
Block a user