diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/CodeGenerator.kt index aec90c0eccc..d246651fc81 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/CodeGenerator.kt @@ -3,7 +3,11 @@ package org.jetbrains.kotlin.backend.native.llvm import kotlin_native.interop.* import llvm.* +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.types.KotlinType @@ -15,20 +19,55 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { return } + if (currentFunction == declaration.descriptor) return + val fn = prolog(declaration) + + var indexOffset = 0 + if (declaration.descriptor is ClassConstructorDescriptor || declaration.descriptor.dispatchReceiverParameter != null) { + val name = "this" + val type = pointerType(LLVMInt8Type()); + val v = alloca(type, name) + store(LLVMGetParam(fn, 0)!!, v) + currentFunction!!.registerVariable(name, v) + indexOffset = 1; + } + + declaration.descriptor.valueParameters.forEachIndexed { i, descriptor -> + val name = descriptor.name.asString() + val type = descriptor.type + val v = alloca(type, name) + store(LLVMGetParam(fn, indexOffset + i)!!, v) + + currentFunction!!.registerVariable(name, v) + } + } + + /* HACK: */ + var currentClass:ClassDescriptor? = null + /* constructor */ + fun initFunction(declaration: IrConstructor) { + function(declaration) + val thisPtr = bitcast(pointerType(classType(declaration.descriptor.containingDeclaration)), load(thisVariable(), tmpVariable()), tmpVariable()) + declaration.descriptor.valueParameters.forEachIndexed { i, descriptor -> + val name = descriptor.name.asString() + val ptr = LLVMBuildStructGEP(context.llvmBuilder, thisPtr, i, tmpVariable()) + val value = load(variable(name)!!, tmpVariable()) + + val typePtr = bitcast(pointerType(LLVMTypeOf(value)), ptr!!, tmpVariable()) + store(value, typePtr!!) + } + currentClass = declaration.descriptor.constructedClass + } + + + private fun prolog(declaration: IrFunction): LLVMOpaqueValue? { index = 0 currentFunction = declaration.descriptor val fn = LLVMAddFunction(context.llvmModule, declaration.descriptor.symbolName, getLlvmFunctionType(declaration.descriptor)) val block = LLVMAppendBasicBlock(fn, "entry") LLVMPositionBuilderAtEnd(context.llvmBuilder, block) function2variables.put(declaration.descriptor, mutableMapOf()) - declaration.descriptor.valueParameters.forEachIndexed { i, descriptor -> - val name = descriptor.name.asString() - val type = descriptor.type - val v = alloca(type, name) - store(LLVMGetParam(fn, i)!!, v) - currentFunction!!.registerVariable(name, v) - } - + return fn } fun tmpVariable():String = currentFunction!!.tmpVariable() @@ -36,8 +75,6 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { val variablesGlobal = mapOf() fun variable(varName:String):LLVMOpaqueValue? = currentFunction!!.variable(varName) - fun FunctionDescriptor.param(num:Int):LLVMOpaqueValue? = LLVMGetParam(llvmFunction.getLlvmValue(), num) - var index:Int = 0 private var FunctionDescriptor.tmpVariableIndex: Int get() = index @@ -53,6 +90,9 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { val FunctionDescriptor.variables: MutableMap get() = this@CodeGenerator.function2variables[this]!! + val ClassConstructorDescriptor.thisValue:LLVMOpaqueValue? + get() = thisValue + fun FunctionDescriptor.registerVariable(varName: String, value:LLVMOpaqueValue?) = variables.put(varName, value) private fun FunctionDescriptor.variable(varName: String): LLVMOpaqueValue? = variables[varName] @@ -62,16 +102,39 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { fun minus(arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, result: String): LLVMOpaqueValue = LLVMBuildSub(context.llvmBuilder, arg0, arg1, result)!! fun div(arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, result: String): LLVMOpaqueValue = LLVMBuildSDiv(context.llvmBuilder, arg0, arg1, result)!! fun remainder(arg0: LLVMOpaqueValue, arg1: LLVMOpaqueValue, result: String): LLVMOpaqueValue = LLVMBuildSRem(context.llvmBuilder, arg0, arg1, result)!! - fun alloca(type: KotlinType, varName: String):LLVMOpaqueValue = LLVMBuildAlloca(context.llvmBuilder, getLLVMType(type), varName)!! + fun alloca(type: KotlinType, varName: String):LLVMOpaqueValue = alloca( getLLVMType(type), varName) + fun alloca(type: LLVMOpaqueType?, varName: String): LLVMOpaqueValue = LLVMBuildAlloca(context.llvmBuilder, type, varName)!! fun load(value:LLVMOpaqueValue, varName: String):LLVMOpaqueValue = LLVMBuildLoad(context.llvmBuilder, value, varName)!! fun store(value:LLVMOpaqueValue, ptr:LLVMOpaqueValue):LLVMOpaqueValue = LLVMBuildStore(context.llvmBuilder, value, ptr)!! fun call(descriptor: FunctionDescriptor, args: MutableList, result: String): LLVMOpaqueValue? { + if (args.size == 0) return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), null, 0, result) memScoped { val rargs = alloc(array[args.size](Ref to LLVMOpaqueValue)) - args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i] } + args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i]} return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), rargs[0], args.size, result) - } + } } + fun bitcast(type: LLVMOpaqueType?, value: LLVMOpaqueValue, variable: String) = LLVMBuildBitCast(context.llvmBuilder, value, type, variable) + + /* to class descriptor */ + fun classType(descriptor: ClassDescriptor):LLVMOpaqueType = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!! + fun typeInfoType(descriptor: ClassDescriptor): LLVMOpaqueType? = descriptor.llvmTypeInfoPtr.getLlvmType() + fun typeInfoValue(descriptor: ClassDescriptor): LLVMOpaqueValue? = descriptor.llvmTypeInfoPtr.getLlvmValue() + + fun superCall(result:String, descriptor:ClassConstructorDescriptor, args:MutableList ):LLVMOpaqueValue? { + val tmp = load(thisVariable(), tmpVariable()) + var rargs:MutableList? = null + if (args.size != 0) + rargs = mutableListOf(tmp, *args.toTypedArray()) + else + rargs = mutableListOf(tmp) + return call(descriptor, rargs, result) + } + + 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) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt index 24d3331d231..bc506080373 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/IrToBitcode.kt @@ -1,15 +1,16 @@ package org.jetbrains.kotlin.backend.native.llvm +import kotlin_native.interop.* import llvm.* import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { @@ -20,8 +21,9 @@ fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { val context = Context(module, runtime, llvmModule) // TODO: dispose - module.accept(RTTIGeneratorVisitor(context), null) - module.accept(CodeGeneratorVisitor(context), null) + module.acceptVoid(RTTIGeneratorVisitor(context)) + context.runtime.importRuntime(llvmModule) + module.acceptVoid(CodeGeneratorVisitor(context)) LLVMWriteBitcodeToFile(llvmModule, outFile) } @@ -46,7 +48,31 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid { } internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid { + val generator = CodeGenerator(context) + + override fun visitConstructor(declaration: IrConstructor) { + generator.initFunction(declaration) + val thisValue = generator.variable("this") + //super.visitConstructor(declaration) + /** + * %this = alloca i8* + * store i8* %0, i8** %this <- prolog + * + * %tmp0 = load i8*, i8** %this <- epilog + * ret i8* %tmp0 + */ + LLVMBuildRet(context.llvmBuilder, generator.load(thisValue!!, generator.tmpVariable())) + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) { + evaluateCall(generator.tmpVariable(), expression) + } + + override fun visitConstructor(declaration: IrConstructor, data: Nothing?) { + super.visitConstructor(declaration, data) + } + override fun visitFunction(declaration: IrFunction) { generator.function(declaration) declaration.acceptChildrenVoid(this) @@ -73,7 +99,30 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateExpression(tmpVariableName: String, value: IrExpression?): LLVMOpaqueValue? { when (value) { is IrCall -> return evaluateCall(tmpVariableName, value) - is IrGetValue -> return generator.load(generator.variable(value.descriptor.name.asString())!!, tmpVariableName) + is IrGetValue -> { + when (value.descriptor) { + is LocalVariableDescriptor, is ValueParameterDescriptor -> { + val variable = generator.variable(value.descriptor.name.asString()) + return generator.load(variable!!, tmpVariableName) + } + is LazyClassReceiverParameterDescriptor -> { + TODO() + } + else -> { + TODO() + } + } + + } + is IrGetField -> { + if (value.descriptor.dispatchReceiverParameter != null) { + val thisPtr = generator.load(generator.thisVariable(), generator.tmpVariable()) + val typedPtr = generator.bitcast(pointerType(generator.classType(generator.currentClass!!)), thisPtr, generator.tmpVariable()) + val fieldPtr = LLVMBuildStructGEP(generator.context.llvmBuilder, typedPtr, generator.indexInClass(value.descriptor), generator.tmpVariable()) + return generator.load(fieldPtr!!, generator.tmpVariable()) + } + TODO() + } null -> return null else -> { TODO() @@ -81,11 +130,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } } - private fun evaluateCall(tmpVariableName: String, value: IrCall?): LLVMOpaqueValue? { - /* TODO: should we count on receiver? - * val tmp = tmpVar() - * val lhs = evaluateExpression(tmp, value.dispatchReceiver!!) - */ + + private fun evaluateCall(tmpVariableName: String, value: IrMemberAccessExpression?): LLVMOpaqueValue? { val args = mutableListOf() value!!.acceptChildrenVoid(object:IrElementVisitorVoid{ override fun visitElement(element: IrElement) { @@ -93,8 +139,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid args.add(evaluateExpression(tmp, element as IrExpression)) } }) - when (value.descriptor) { - is FunctionDescriptor -> return evaluateFunctionCall(tmpVariableName, value, args) + when { + value is IrDelegatingConstructorCall -> return generator.superCall(tmpVariableName, value.descriptor, args) + value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(tmpVariableName, value as IrCall, args) else -> { TODO() } @@ -104,7 +151,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } private fun evaluateSimpleFunctionCall(tmpVariableName: String, value: IrCall, args: MutableList): LLVMOpaqueValue? { - return generator.call(value.descriptor as org.jetbrains.kotlin.descriptors.FunctionDescriptor, args, tmpVariableName) + return generator.call(value.descriptor as FunctionDescriptor, args, tmpVariableName) } @@ -112,12 +159,26 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val descriptor:FunctionDescriptor = callee.descriptor as FunctionDescriptor when { descriptor.isOperator -> return evaluateOperatorCall(tmpVariableName, callee, args) + descriptor is ClassConstructorDescriptor -> return evaluateConstructorCall(tmpVariableName, callee, args) else -> { return evaluateSimpleFunctionCall(tmpVariableName, callee, args) } } } + private fun evaluateConstructorCall(variableName: String, callee: IrCall, args: MutableList): LLVMOpaqueValue? { + memScoped { + val params = allocNativeArrayOf(LLVMOpaqueValue, generator.typeInfoValue((callee.descriptor as ClassConstructorDescriptor).containingDeclaration), Int32(1).getLlvmValue()) + val thisValue = LLVMBuildCall(context.llvmBuilder, context.runtime.allocInstanceFunction, params[0], 2, variableName) + + + val constructorParams: MutableList = mutableListOf() + constructorParams += thisValue + constructorParams += args + return generator.call(callee.descriptor as FunctionDescriptor, constructorParams, variableName) + } + } + private fun evaluateOperatorCall(tmpVariableName: String, callee: IrCall, args: MutableList): LLVMOpaqueValue { when (callee!!.origin) { IrStatementOrigin.PLUS -> return generator.plus(args[0]!!, args[1]!!, tmpVariableName) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt index 8fca993ec9f..dd69405acae 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/LlvmUtils.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.native.llvm import kotlin_native.interop.* import llvm.* +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.utils.singletonOrEmptyList @@ -75,12 +76,17 @@ internal fun getLlvmFunctionType(function: FunctionDescriptor): LLVMOpaqueType? function.extensionReceiverParameter.singletonOrEmptyList() + function.valueParameters - val paramTypes = params.map { getLLVMType(it.type) }.toTypedArray() - if (paramTypes.size == 0) return LLVMFunctionType(returnType, null, 0, 0) + var extraParam = listOf() + if (function is ClassConstructorDescriptor) { + extraParam += pointerType(LLVMInt8Type()) + } + val paramTypes:List = params.map { getLLVMType(it.type) } + extraParam += paramTypes + if (extraParam.size == 0) return LLVMFunctionType(returnType, null, 0, 0) memScoped { - val paramTypesPtr = allocNativeArrayOf(LLVMOpaqueType, *paramTypes)[0] - return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0) + val paramTypesPtr = mallocNativeArrayOf(LLVMOpaqueType, *extraParam.toTypedArray())[0] // TODO: dispose + return LLVMFunctionType(returnType, paramTypesPtr, extraParam.size, 0) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt index a5646d4dead..8e527a6b3df 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/NameUtils.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.backend.native.llvm import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind.* import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.constants.StringValue @@ -21,5 +22,12 @@ internal val FunctionDescriptor.symbolName: String return "kfun:" + this.fqNameSafe.toString() // FIXME: add signature } +internal val ClassDescriptor.symbolName: String + get() = when (this.kind) { + CLASS -> "kclass:" + INTERFACE -> "kinf:" + else -> TODO("fixme") + } + fqNameSafe + internal val ClassDescriptor.typeInfoSymbolName: String get() = "ktype:" + this.fqNameSafe.toString() \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt index faf748978ae..38cf961ad48 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/native/llvm/Runtime.kt @@ -34,6 +34,15 @@ class Runtime(private val bitcodeFile: String) { val fieldTableRecordType = LLVMGetTypeByName(llvmModule, "struct.FieldTableRecord") val methodTableRecordType = LLVMGetTypeByName(llvmModule, "struct.MethodTableRecord") val globalhHashType = LLVMGetTypeByName(llvmModule, "struct.GlobalHash") + val allocInstanceFunction = LLVMGetNamedFunction(llvmModule, "AllocInstance") + + fun importRuntime(module: LLVMOpaqueModule) { + memScoped { + val params = allocNativeArrayOf(LLVMOpaqueType, pointerType(typeInfoType), LLVMInt32Type()) + val functionAllocInstanceType = LLVMFunctionType(pointerType(LLVMInt8Type()),params[0], 2, 0) + LLVMAddFunction(module, "AllocInstance", functionAllocInstanceType) + } + } val target = LLVMGetTarget(llvmModule)!!.asCString().toString() diff --git a/backend.native/tests/Makefile b/backend.native/tests/Makefile index 23c2935b307..9ad05e228fa 100644 --- a/backend.native/tests/Makefile +++ b/backend.native/tests/Makefile @@ -1,10 +1,11 @@ TESTS := sum \ sum_foo_bar \ - arithmetic + arithmetic \ + initialization BIN_TESTS=$(foreach t, ${TESTS},${t}_test) -VPATH=codegen/function:${TOP}/runtime/build +VPATH=codegen/function:codegen/object:${TOP}/runtime/build LLC=llc-mp-3.8 CC=clang-mp-3.8 diff --git a/backend.native/tests/codegen/object/initialization-test.c b/backend.native/tests/codegen/object/initialization-test.c new file mode 100644 index 00000000000..86646705db9 --- /dev/null +++ b/backend.native/tests/codegen/object/initialization-test.c @@ -0,0 +1,10 @@ +extern void *resolve_symbol(const char*); + +int +run_test() { + int (*foo)(int, int) = resolve_symbol("kfun:foo"); + + if (foo(2, 3) != 2) return 1; + + return 0; +} diff --git a/backend.native/tests/codegen/object/initialization.kt b/backend.native/tests/codegen/object/initialization.kt new file mode 100644 index 00000000000..6507fa2d7da --- /dev/null +++ b/backend.native/tests/codegen/object/initialization.kt @@ -0,0 +1,10 @@ +open class A(val a:Int, val b:Int) + +open class B(val c:Int, d:Int):A(c, d) + +//class C():B(42) + +fun foo(i:Int, j:Int):Int { + val b = B(i, j) + return b.c +} \ No newline at end of file