diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 51be96701b2..cb58c7d7554 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -43,31 +43,7 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { } } - /* HACK: */ - var currentClass:ClassDescriptor? = null - /* constructor */ - fun initFunction(declaration: IrConstructor) { - function(declaration) - val thisPtr = bitcast(pointerType(classType(declaration.descriptor.containingDeclaration)), load(thisVariable(), newVar()), newVar()) - - /** - * TODO: check shadowing. - */ - declaration.descriptor.containingDeclaration.fields.forEachIndexed { i, descriptor -> - val name = descriptor.name.asString() - - if (!declaration.descriptor.valueParameters.any { it -> it.name.asString() == name }) - return@forEachIndexed - - val ptr = LLVMBuildStructGEP(context.llvmBuilder, thisPtr, i, newVar()) - val value = load(variable(name)!!, newVar()) - - val typePtr = bitcast(pointerType(LLVMTypeOf(value)), ptr!!, newVar()) - store(value, typePtr!!) - } - currentClass = declaration.descriptor.constructedClass - } - + fun fields(descriptor: ClassDescriptor):List = descriptor.fields private fun prolog(declaration: IrFunction): LLVMOpaqueValue? { variableIndex = 0 @@ -171,7 +147,7 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { fun thisVariable() = variable("this")!! fun param(fn: FunctionDescriptor?, i: Int): LLVMOpaqueValue? = LLVMGetParam(fn!!.llvmFunction.getLlvmValue(), i) - fun indexInClass(p:PropertyDescriptor):Int = currentClass!!.fields.indexOf(p) + fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p) fun basicBlock(): LLVMOpaqueBasicBlock? = LLVMAppendBasicBlock(currentFunction!!.llvmFunction.getLlvmValue(), currentFunction!!.bbLabel()) 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 5e7f30a4274..4abf0e6271f 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 @@ -148,17 +148,28 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// override fun visitConstructor(declaration: IrConstructor) { - codegen.initFunction(declaration) - val thisValue = codegen.variable("this") - //super.visitConstructor(declaration) - /** - * %this = alloca i8* - * store i8* %0, i8** %this <- prolog - * - * %tmp0 = load i8*, i8** %this <- epilog - * ret i8* %tmp0 - */ - codegen.ret(codegen.load(thisValue!!, codegen.newVar())) + codegen.function(declaration) + val classDescriptor = DescriptorUtils.getContainingClass(declaration.descriptor) + val thisPtr = codegen.load(codegen.thisVariable(), codegen.newVar()) + val typeOfClass = classDescriptor!!.defaultType + val names = typeOfClass.memberScope.getVariableNames() + val fields = codegen.fields(classDescriptor).toSet() + declaration.descriptor.valueParameters + .filter { names.contains(it.name) } // selects only parameters that match with declared class variables + .map { // maps parameter to list descriptors + val variables = typeOfClass.memberScope.getContributedVariables(it.name, NoLookupLocation.FROM_BACKEND) + assert(variables.size == 1) + variables.first() } + .filter {fields.contains(it)} // filter only fields that contains backing store + .forEach { // store parameters to backing storage + val fieldPtr = fieldPtrOfClass(thisPtr, it) + val variable = codegen.variable(it.name.asString()) + val value = codegen.load(variable!!, codegen.newVar()) + codegen.store(value, fieldPtr!!) + } + + /* TODO: body */ + codegen.ret(thisPtr) logger.log("visitConstructor : ${ir2string(declaration)}") } @@ -450,7 +461,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid logger.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null) { val thisPtr = codegen.load(codegen.thisVariable(), codegen.newVar()) - return codegen.load(fieldPtrOfClass(thisPtr, value)!!, codegen.newVar()) + return codegen.load(fieldPtrOfClass(thisPtr, value.descriptor)!!, codegen.newVar()) } else { val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!! @@ -465,7 +476,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid 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)!!) + return codegen.store(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)!!) } else { val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName) @@ -507,14 +518,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } */ - private fun fieldPtrOfClass(thisPtr: LLVMOpaqueValue, value: IrFieldAccessExpression): LLVMOpaqueValue? { + private fun fieldPtrOfClass(thisPtr: LLVMOpaqueValue, value: PropertyDescriptor): LLVMOpaqueValue? { val objHeaderPtr = codegen.bitcast(kObjHeaderPtr, thisPtr, codegen.newVar()) - val typePtr = pointerType(codegen.classType(codegen.currentClass!!)) + val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor)) memScoped { - val args = allocNativeArrayOf(LLVMOpaqueValue, Int32(1).getLlvmValue()) + val args = allocNativeArrayOf(LLVMOpaqueValue, kImmOne) 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()) + val fieldPtr = LLVMBuildStructGEP(codegen.context.llvmBuilder, typedObjPtr, codegen.indexInClass(value), codegen.newVar()) return fieldPtr } } @@ -643,6 +654,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private val kLteq0 = Name.identifier("LTEQ0") private val kNot = Name.identifier("NOT") private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!! + private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!! private val kTrue = LLVMConstInt(LLVMInt1Type(), 1, 1)!! private val kFalse = LLVMConstInt(LLVMInt1Type(), 0, 1)!! diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 80f82eb5d77..a54458eea07 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -209,18 +209,23 @@ task run() { task sum (type:UnitKonanTest) { source = "codegen/function/sum.kt" } -// FIXME (https://github.com/JetBrains/kotlin-native/pull/74) -//task method_call(type: UnitKonanTest) { -// source = "codegen/object/method_call.kt" -//} + +task method_call(type: UnitKonanTest) { + source = "codegen/object/method_call.kt" +} task fields(type: UnitKonanTest) { source = "codegen/object/fields.kt" } -/*task objectInitialization(type: UnitKonanTest) { + +task constructor(type: UnitKonanTest) { + source = "codegen/object/constructor.kt" +} + +task objectInitialization(type: UnitKonanTest) { source = "codegen/object/initialization.kt" -}*/ +} /* task objectBasic(type: KonanTest) { source = "$codegen/klass/basic.kt" diff --git a/backend.native/tests/codegen/object/constructor-test.c b/backend.native/tests/codegen/object/constructor-test.c new file mode 100644 index 00000000000..05809dc36f6 --- /dev/null +++ b/backend.native/tests/codegen/object/constructor-test.c @@ -0,0 +1,38 @@ +#include + +extern void *resolve_symbol(const char*); +extern void *AllocInstance(void *, int32_t); + +#define CHECK_OBJ(obj, info) if ((uintptr_t)*(obj) != (uintptr_t)(info)) return 1; +#define MAGIC0 0xdeadbeef +#define MAGIC1 0xcafebabe +#define MAGIC2 0xabadbabe + +int +run_test() { + void *a_type_info = resolve_symbol("ktype:A"); + void *(*a_init)(void *) = resolve_symbol("kfun:A.()"); + void *b_type_info = resolve_symbol("ktype:B"); + void *(*b_init)(void *, int) = resolve_symbol("kfun:B.(Int)"); + int (*b_get_a)(void *) = resolve_symbol("kfun:B.()"); + void *c_type_info = resolve_symbol("ktype:C"); + void *(*c_init)(void *, int, int) = resolve_symbol("kfun:C.(Int;Int)"); + int (*c_get_a)(void *) = resolve_symbol("kfun:C.()"); + + void **a = (void **)AllocInstance(a_type_info, 1); + CHECK_OBJ(a, a_type_info) + + void **b = (void **)AllocInstance(b_type_info, 1); + CHECK_OBJ(b, b_type_info) + void *b_obj = b_init((void *)b, MAGIC0); + CHECK_OBJ(b, b_type_info) + if (b_get_a(b_obj) != MAGIC0) return 1; + + + void **c = (void **)AllocInstance(c_type_info, 1); + CHECK_OBJ(c, c_type_info) + void *c_obj = c_init((void *)c, 0xcafebabe, 0xabadbabe); + CHECK_OBJ(c, c_type_info) + if (c_get_a(c_obj) != MAGIC1) return 1; + return 0; +} diff --git a/backend.native/tests/codegen/object/constructor.kt b/backend.native/tests/codegen/object/constructor.kt new file mode 100644 index 00000000000..2ca97303454 --- /dev/null +++ b/backend.native/tests/codegen/object/constructor.kt @@ -0,0 +1,5 @@ +class A() + +class B(val a:Int) + +class C(val a:Int, b:Int) \ No newline at end of file