Support for object properties access implemented. Minor refactoring
following code:
--------8<--------
> cat ../backend.native/tests/codegen/object/method_call.kt
class A(val a:Int) {
fun foo(i:Int) = a + i
}
fun fortyTwo() = A(41).foo(1)
--------8<--------
translator generates following getter
--------8<--------
> llvm-dis-mp-3.8 ../backend.native/tests/codegen/object/method_call.kt.bc -o -
; ModuleID = '../backend.native/tests/codegen/object/method_call.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 i32 @"kfun:A.<get-a>()"(i8*) {
entry:
%this = alloca i8*
store i8* %0, i8** %this
%tmp_1 = load i8*, i8** %this
%tmp_2 = bitcast i8* %tmp_1 to %struct.ObjHeader*
%tmp_3 = getelementptr %struct.ObjHeader, %struct.ObjHeader* %tmp_2, i32 1
%tmp_4 = bitcast %struct.ObjHeader* %tmp_3 to %"kclass:A"*
%tmp_5 = getelementptr inbounds %"kclass:A", %"kclass:A"* %tmp_4, i32 0, i32 0
%tmp_6 = load i32, i32* %tmp_5
ret i32 %tmp_6
}
...
--------8<--------
similar history for global properties:
--------8<--------
> cat ../backend.native/tests/codegen/object/fields.kt
private var globalValue = 1
var global:Int
get() = globalValue
set(value:Int) {globalValue = value}
fun globalTest(i:Int):Int {
global += i
return global
}
--------8<--------
translator generates following code:
--------8<--------
> llvm-dis-mp-3.8 ../backend.native/tests/codegen/object/fields.kt.bc -o -
; ModuleID = '../backend.native/tests/codegen/object/fields.kt.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"
...
@"kvar:<root>.globalValue" = global i32 1
...
define i32 @"kfun:<get-globalValue>()"() {
entry:
%tmp_1 = load i32, i32* @"kvar:<root>.globalValue"
ret i32 %tmp_1
}
define void @"kfun:<set-globalValue>(Int)"(i32) {
entry:
%"<set-?>" = alloca i32
store i32 %0, i32* %"<set-?>"
%tmp_0 = load i32, i32* %"<set-?>"
store i32 %tmp_0, i32* @"kvar:<root>.globalValue"
ret void
}
define i32 @"kfun:<get-global>()"() {
entry:
%tmp_0 = call i32 @"kfun:<get-globalValue>()"()
ret i32 %tmp_0
}
define void @"kfun:<set-global>(Int)"(i32) {
entry:
%value = alloca i32
store i32 %0, i32* %value
%tmp_0 = load i32, i32* %value
call void @"kfun:<set-globalValue>(Int)"(i32 %tmp_0)
ret void
}
--------8<--------
This commit is contained in:
committed by
vvlevchenko
parent
f80697817e
commit
0fff59a516
+176
-49
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBinaryPrimitiveImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
@@ -69,6 +70,8 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
|
||||
|
||||
val codegen = CodeGenerator(context)
|
||||
@@ -179,7 +182,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
logger.log("visitCall ${ir2string(expression)}")
|
||||
logger.log("visitCall : ${ir2string(expression)}")
|
||||
val isUnit = KotlinBuiltIns.isUnit(expression.descriptor.returnType!!)
|
||||
val tmpVariable = if (isUnit) "" else codegen.newVar()
|
||||
evaluateExpression(tmpVariable, expression)
|
||||
@@ -245,40 +248,73 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
// top level setter for field
|
||||
override fun visitSetField(expression: IrSetField) {
|
||||
logger.log("visitSetField : ${ir2string(expression)}")
|
||||
evaluateExpression("", expression)
|
||||
}
|
||||
|
||||
private fun evaluateExpression(tmpVariableName: String, value: IrElement?): LLVMOpaqueValue? {
|
||||
when (value) {
|
||||
is IrCall -> return evaluateCall (tmpVariableName, value)
|
||||
is IrGetValue -> return evaluateGetValue (tmpVariableName, value)
|
||||
is IrSetVariable -> return evaluateSetVariable ( value)
|
||||
is IrVariable -> return evaluateVariable ( value)
|
||||
is IrGetField -> return evaluateGetField ( value)
|
||||
is IrConst<*> -> return evaluateConst ( value)
|
||||
is IrReturn -> return evaluateReturn ( value)
|
||||
is IrBlock -> return evaluateBlock ( value)
|
||||
null -> return null
|
||||
else -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------//
|
||||
// top level getter for field:
|
||||
// normally GetField appears in following manner:
|
||||
//
|
||||
// PROPERTY private var globalValue: kotlin.Int
|
||||
// FIELD PROPERTY_BACKING_FIELD private var globalValue: kotlin.Int
|
||||
// EXPRESSION_BODY
|
||||
// CONST Int type=kotlin.Int value='1'
|
||||
// FUN DEFAULT_PROPERTY_ACCESSOR private fun <get-globalValue>(): kotlin.Int
|
||||
// BLOCK_BODY
|
||||
// RETURN type=kotlin.Nothing from='<get-globalValue>(): Int'
|
||||
// GET_FIELD 'globalValue: Int' type=kotlin.Int origin=null
|
||||
//
|
||||
// and this case processed via evaluateExpression from RETURN processing,
|
||||
// where we process similar thing with generating temporal variable to return
|
||||
// alternatively top level IR, let's assume following IR:
|
||||
//
|
||||
// PROPERTY private var globalValue: kotlin.Int
|
||||
// FUN DEFAULT_PROPERTY_ACCESSOR private fun <get-globalValue>(): kotlin.Int
|
||||
// BLOCK_BODY
|
||||
// GET_FIELD 'globalValue: Int' type=kotlin.Int origin=null
|
||||
//
|
||||
// in this situation our visitGetField will be charged, that why no
|
||||
// temporal variable is required.
|
||||
override fun visitGetField(expression: IrGetField) {
|
||||
logger.log("visitGetField : ${ir2string(expression)}")
|
||||
evaluateExpression("", expression)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateCall(tmpVariableName: String, value: IrMemberAccessExpression?): LLVMOpaqueValue? {
|
||||
logger.log("evaluateCall : $tmpVariableName = ${ir2string(value)}")
|
||||
val args = mutableListOf<LLVMOpaqueValue?>() // Create list of function args.
|
||||
value!!.acceptChildrenVoid(object: IrElementVisitorVoid { // Iterate args of the function.
|
||||
override fun visitElement(element: IrElement) { // Visit arg.
|
||||
val tmp = codegen.newVar() // Create variable representing the arg in codegen
|
||||
args.add(evaluateExpression(tmp, element as IrExpression)) // Evaluate expression and get LLVM arg
|
||||
}
|
||||
})
|
||||
override fun visitField(expression: IrField) {
|
||||
logger.log("visitField : ${ir2string(expression)}")
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor.containingDeclaration is PackageFragmentDescriptor) {
|
||||
val globalProperty = LLVMAddGlobal(context.llvmModule, getLLVMType(descriptor.type), descriptor.symbolName)
|
||||
LLVMSetInitializer(globalProperty, evaluateExpression("", expression.initializer))
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(tmpVariableName, value as IrCall, args)
|
||||
value is IrDelegatingConstructorCall -> return superCall(tmpVariableName, value.descriptor, args)
|
||||
else -> {
|
||||
super.visitField(expression)
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateExpression(tmpVariableName: String, value: IrElement?): LLVMOpaqueValue? {
|
||||
when (value) {
|
||||
is IrSetterCallImpl -> return evaluateSetterCall (tmpVariableName, value)
|
||||
is IrCall -> return evaluateCall (tmpVariableName, value)
|
||||
is IrGetValue -> return evaluateGetValue (tmpVariableName, value)
|
||||
is IrSetVariable -> return evaluateSetVariable ( value)
|
||||
is IrVariable -> return evaluateVariable ( value)
|
||||
is IrGetField -> return evaluateGetField ( value)
|
||||
is IrSetField -> return evaluateSetField ( value)
|
||||
is IrConst<*> -> return evaluateConst ( value)
|
||||
is IrReturn -> return evaluateReturn ( value)
|
||||
is IrBlock -> return evaluateBlock ( value)
|
||||
is IrExpressionBody -> return evaluateExpression (tmpVariableName, value.expression)
|
||||
null -> return null
|
||||
else -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
@@ -331,17 +367,74 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
private fun evaluateGetField(value: IrGetField): LLVMOpaqueValue {
|
||||
logger.log("evaluateGetField : ${ir2string(value)}")
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
if (value.descriptor.getter != null) {
|
||||
val tmpThis = codegen.load(codegen.thisVariable(), codegen.newVar())
|
||||
return evaluateSimpleFunctionCall(codegen.newVar(), value.descriptor.getter!!.original, listOf(tmpThis))!!
|
||||
} else {
|
||||
val thisPtr = codegen.load(codegen.thisVariable(), codegen.newVar())
|
||||
val typedPtr = codegen.bitcast(pointerType(codegen.classType(codegen.currentClass!!)), thisPtr, codegen.newVar())
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.context.llvmBuilder, typedPtr, codegen.indexInClass(value.descriptor), codegen.newVar())
|
||||
return codegen.load(fieldPtr!!, codegen.newVar())
|
||||
}
|
||||
val thisPtr = codegen.load(codegen.thisVariable(), codegen.newVar())
|
||||
return codegen.load(fieldPtrOfClass(thisPtr, value)!!, codegen.newVar())
|
||||
}
|
||||
else {
|
||||
val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!!
|
||||
return codegen.load(ptr, codegen.newVar())
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSetField(value: IrSetField): LLVMOpaqueValue {
|
||||
logger.log("evaluateSetField : ${ir2string(value)}")
|
||||
val valueToAssign = evaluateExpression(codegen.newVar(), value.value)!!
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
val thisPtr = codegen.load(codegen.thisVariable(), codegen.newVar())
|
||||
return codegen.store(valueToAssign, fieldPtrOfClass(thisPtr, value)!!)
|
||||
}
|
||||
else {
|
||||
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
|
||||
return codegen.store(valueToAssign, globalValue!!)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
/*
|
||||
in C:
|
||||
struct ObjHeader *header = (struct ObjHeader *)ptr;
|
||||
struct T* obj = (T*)&header[1];
|
||||
return &obj->fieldX;
|
||||
|
||||
in llvm ir:
|
||||
%struct.ObjHeader = type { i32, i32 }
|
||||
%struct.Object = type { i32, i32 }
|
||||
|
||||
; Function Attrs: nounwind ssp uwtable
|
||||
define i32 @fooField2(i8*) #0 {
|
||||
%2 = alloca i8*, align 8
|
||||
%3 = alloca %struct.ObjHeader*, align 8
|
||||
%4 = alloca %struct.Object*, align 8
|
||||
store i8* %0, i8** %2, align 8
|
||||
%5 = load i8*, i8** %2, align 8
|
||||
%6 = bitcast i8* %5 to %struct.ObjHeader*
|
||||
store %struct.ObjHeader* %6, %struct.ObjHeader** %3, align 8
|
||||
%7 = load %struct.ObjHeader*, %struct.ObjHeader** %3, align 8
|
||||
|
||||
%8 = getelementptr inbounds %struct.ObjHeader, %struct.ObjHeader* %7, i64 1; <- (T*)&header[1];
|
||||
|
||||
%9 = bitcast %struct.ObjHeader* %8 to %struct.Object*
|
||||
store %struct.Object* %9, %struct.Object** %4, align 8
|
||||
%10 = load %struct.Object*, %struct.Object** %4, align 8
|
||||
%11 = getelementptr inbounds %struct.Object, %struct.Object* %10, i32 0, i32 0 <- &obj->fieldX
|
||||
%12 = load i32, i32* %11, align 4
|
||||
ret i32 %12
|
||||
}
|
||||
|
||||
*/
|
||||
private fun fieldPtrOfClass(thisPtr: LLVMOpaqueValue, value: IrFieldAccessExpression): LLVMOpaqueValue? {
|
||||
val objHeaderPtr = codegen.bitcast(kObjHeaderPtr, thisPtr, codegen.newVar())
|
||||
val typePtr = pointerType(codegen.classType(codegen.currentClass!!))
|
||||
memScoped {
|
||||
val args = allocNativeArrayOf(LLVMOpaqueValue, Int32(1).getLlvmValue())
|
||||
val objectPtr = LLVMBuildGEP(codegen.context.llvmBuilder, objHeaderPtr, args[0], 1, codegen.newVar())
|
||||
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!, codegen.newVar())
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.context.llvmBuilder, typedObjPtr, codegen.indexInClass(value.descriptor), codegen.newVar())
|
||||
return fieldPtr
|
||||
}
|
||||
TODO()
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -387,12 +480,23 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSimpleFunctionCall(tmpVariableName: String, descriptor: FunctionDescriptor, args: List<LLVMOpaqueValue?>): LLVMOpaqueValue? {
|
||||
//logger.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}")
|
||||
if (descriptor.isOverridable)
|
||||
return callVirtual(descriptor, args, tmpVariableName)
|
||||
else
|
||||
return callDirect(descriptor, args, tmpVariableName)
|
||||
private fun evaluateCall(tmpVariableName: String, value: IrMemberAccessExpression?): LLVMOpaqueValue? {
|
||||
logger.log("evaluateCall : $tmpVariableName = ${ir2string(value)}")
|
||||
val args = mutableListOf<LLVMOpaqueValue?>() // Create list of function args.
|
||||
value!!.acceptChildrenVoid(object: IrElementVisitorVoid { // Iterate args of the function.
|
||||
override fun visitElement(element: IrElement) { // Visit arg.
|
||||
val tmp = codegen.newVar() // Create variable representing the arg in codegen
|
||||
args.add(evaluateExpression(tmp, element as IrExpression)) // Evaluate expression and get LLVM arg
|
||||
}
|
||||
})
|
||||
|
||||
when {
|
||||
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(tmpVariableName, value as IrCall, args)
|
||||
value is IrDelegatingConstructorCall -> return superCall(tmpVariableName, value.descriptor, args)
|
||||
else -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -408,6 +512,27 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSimpleFunctionCall(tmpVariableName: String, descriptor: FunctionDescriptor, args: List<LLVMOpaqueValue?>): LLVMOpaqueValue? {
|
||||
//logger.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}")
|
||||
if (descriptor.isOverridable)
|
||||
return callVirtual(descriptor, args, tmpVariableName)
|
||||
else
|
||||
return callDirect(descriptor, args, tmpVariableName)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSetterCall(tmpVariableName: String, value: IrSetterCallImpl): LLVMOpaqueValue? {
|
||||
val descriptor = value.descriptor as FunctionDescriptor
|
||||
val args = mutableListOf<LLVMOpaqueValue?>()
|
||||
if (descriptor.dispatchReceiverParameter != null)
|
||||
args.add(evaluateExpression(codegen.newVar(), value.dispatchReceiver)) //add this ptr
|
||||
args.add(evaluateExpression(codegen.newVar(), value.getValueArgument(0)))
|
||||
return evaluateSimpleFunctionCall(tmpVariableName, descriptor, args)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateConstructorCall(variableName: String, callee: IrCall, args: List<LLVMOpaqueValue?>): LLVMOpaqueValue? {
|
||||
logger.log("evaluateConstructorCall : $variableName = ${ir2string(callee)}")
|
||||
memScoped {
|
||||
@@ -533,10 +658,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
/* Runtime constant */
|
||||
private val kTypeInfo = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
|
||||
private val kTypeInfoPtr = pointerType(kTypeInfo)
|
||||
private val kInt8Ptr = pointerType(LLVMInt8Type())
|
||||
private val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||
private val kTypeInfo = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
|
||||
private val kObjHeader = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!!
|
||||
private val kObjHeaderPtr = pointerType(kObjHeader)!!
|
||||
private val kTypeInfoPtr = pointerType(kTypeInfo)
|
||||
private val kInt8Ptr = pointerType(LLVMInt8Type())
|
||||
private val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
|
||||
+4
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind.*
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
@@ -54,3 +55,6 @@ internal val ClassDescriptor.symbolName: String
|
||||
|
||||
internal val ClassDescriptor.typeInfoSymbolName: String
|
||||
get() = "ktype:" + this.fqNameSafe.toString()
|
||||
|
||||
internal val PropertyDescriptor.symbolName:String
|
||||
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
|
||||
Reference in New Issue
Block a user