From c47a58d34f833c15cf79976f9bd8f69e0e7b2491 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 10 Nov 2016 18:51:11 +0300 Subject: [PATCH] codegen: implementation virtual calls and bit refactoring... callVirtual and callDirect become members of Ir2Bitcode. some details: this is runtime based function resolving will be replaced with compile time soon. example: -----------------8<----------------- > cat ../backend.native/tests/runtime/basic/hello2.kt // TODO: remove kotlin_native.io once overrides are in place. fun main(args : Array) { print("you entered '" + readLine() + "'") } -----------------8<----------------- translator generates following (reduced) -----------------8<----------------- > llvm-dis-mp-3.8 ../backend.native/tests/runtime/basic/hello2.kt.bc -o - ; ModuleID = '../backend.native/tests/runtime/basic/hello2.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 void @"kfun:main"(i8*) { entry: %args = alloca i8* store i8* %0, i8** %args %tmp_3 = call i8* @Kotlin_io_Console_readLine() %tmp_1 = call i8* @"kfun:kotlin.String.plus"(i8* @"kstr:bwW55yLpGKEB25tD4IQg8ZiUSe0=", i8* %tmp_3) %tmp_0 = call i8* @"kfun:kotlin.String.plus"(i8* %tmp_1, i8* @"kstr:u1idBiHlRy9HD6NCWiNMdLHiAug=") call void @Kotlin_io_Console_print(i8* %tmp_0) ret void } declare i8* @Kotlin_io_Console_readLine() define i8* @"kfun:kotlin.String.plus"(i8*, i8*) { entry: %this = alloca i8* store i8* %0, i8** %this %other = alloca i8* store i8* %1, i8** %other %tmp_1 = load i8*, i8** %this %tmp_3 = load i8*, i8** %other %tmp_4 = bitcast i8* %tmp_3 to i8** %tmp_5 = load i8*, i8** %tmp_4 %tmp_6 = bitcast i8* %tmp_5 to %struct.TypeInfo* %tmp_7 = call i8* @LookupOpenMethod(%struct.TypeInfo* %tmp_6, i64 5382438289122769377) <-- runtime resolve method %tmp_8 = bitcast i8* %tmp_7 to i8* (i8*)* %tmp_2 = call i8* %tmp_8(i8* %tmp_3) %tmp_0 = call i8* @Kotlin_String_plusImpl(i8* %tmp_1, i8* %tmp_2) ret i8* %tmp_0 } -----------------8<----------------- --- .../backend/konan/llvm/CodeGenerator.kt | 28 +++++----- .../kotlin/backend/konan/llvm/Context.kt | 2 + .../kotlin/backend/konan/llvm/IrToBitcode.kt | 52 ++++++++++++++++++- 3 files changed, 65 insertions(+), 17 deletions(-) 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 c1661a67cbc..ee3fc0e3c11 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 @@ -138,15 +138,19 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { 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) + //-------------------------------------------------------------------------// + + fun call(llvmFunction: LLVMOpaqueValue?, args: MutableList, result: String?): LLVMOpaqueValue? { + if (args.size == 0) return LLVMBuildCall(context.llvmBuilder, llvmFunction, null, 0, result) memScoped { val rargs = alloc(array[args.size](Ref to LLVMOpaqueValue)) - args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i]} - return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), rargs[0], args.size, result) - } + args.forEachIndexed { i, llvmOpaqueValue -> rargs[i].value = args[i] } + return LLVMBuildCall(context.llvmBuilder, llvmFunction, rargs[0], args.size, result) + } } + //-------------------------------------------------------------------------// + fun trap() { // LLVM doesn't seem to provide API to get intrinsics; // workaround this by declaring the intrinsic explicitly: @@ -165,16 +169,6 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { 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) @@ -182,6 +176,10 @@ internal class CodeGenerator(override val context:Context) : ContextUtils { fun basicBlock(): LLVMOpaqueBasicBlock? = LLVMAppendBasicBlock(currentFunction!!.llvmFunction.getLlvmValue(), currentFunction!!.bbLabel()) fun lastBasicBlock(): LLVMOpaqueBasicBlock? = LLVMGetLastBasicBlock(currentFunction!!.llvmFunction.getLlvmValue()) + + fun functionLlvmValue(descriptor: FunctionDescriptor) = descriptor.llvmFunction.getLlvmValue() + fun functionHash(descriptor: FunctionDescriptor): LLVMOpaqueValue? = descriptor.functionName.localHash.getLlvmValue() + } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Context.kt index 06027b44d83..dcad7bbbd1e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Context.kt @@ -26,6 +26,8 @@ internal class Context(val irModule: IrModuleFragment, val runtime: Runtime, val val allocInstanceFunction = importRtFunction("AllocInstance") val allocArrayFunction = importRtFunction("AllocArrayInstance") + val lookupFieldOffset = importRtFunction("LookupFieldOffset") + val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod") fun dispose() { LLVMDisposeBuilder(llvmBuilder) 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 f27f710063f..9b09bdb0d70 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 @@ -276,7 +276,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid when { value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(tmpVariableName, value as IrCall, args) - value is IrDelegatingConstructorCall -> return generator.superCall(tmpVariableName, value.descriptor, args) + value is IrDelegatingConstructorCall -> return superCall(tmpVariableName, value.descriptor, args) else -> { TODO() } @@ -388,7 +388,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateSimpleFunctionCall(tmpVariableName: String, descriptor: FunctionDescriptor, args: MutableList): LLVMOpaqueValue? { //logger.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}") - return generator.call(descriptor, args, tmpVariableName) + if (descriptor.isOverridable) + return callVirtual(descriptor, args, tmpVariableName) + else + return callDirect(descriptor, args, tmpVariableName) } //-------------------------------------------------------------------------// @@ -483,4 +486,49 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid branch.condition is IrConst<*> // If branch condition is constant. && (branch.condition as IrConst<*>).value as Boolean // If condition is "true" + //-------------------------------------------------------------------------// + + fun callDirect(descriptor: FunctionDescriptor, args: MutableList, result: String?): LLVMOpaqueValue? { + val llvmFunction = generator.functionLlvmValue(descriptor) + return generator.call(llvmFunction, args, result) + } + + //-------------------------------------------------------------------------// + + /* 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) + + //-------------------------------------------------------------------------// + + fun callVirtual(descriptor: FunctionDescriptor, args: MutableList, result: String?): LLVMOpaqueValue? { + val thisI8PtrPtr = generator.bitcast(kInt8PtrPtr, args[0]!!, generator.tmpVariable()) // Cast "this (i8*)" to i8**. + val typeInfoI8Ptr = generator.load(thisI8PtrPtr!!, generator.tmpVariable()) // Load TypeInfo address. + val typeInfoPtr = generator.bitcast(kTypeInfoPtr, typeInfoI8Ptr, generator.tmpVariable()) // Cast TypeInfo (i8*) to TypeInfo*. + val methodHash = generator.functionHash(descriptor) // Calculate hash of the method to be invoked + val lookupArgs = mutableListOf(typeInfoPtr, methodHash) // Prepare args for lookup + val llvmMethod = generator.call( + context.lookupOpenMethodFunction, + lookupArgs, + generator.tmpVariable()) // Get method ptr to be invoked + + val functionPtrType = pointerType(getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked + val function = generator.bitcast(functionPtrType, llvmMethod!!, generator.tmpVariable()) // Cast method address to the type + return generator.call(function, args, result) // Invoke the method + } + + //-------------------------------------------------------------------------// + + fun superCall(result:String, descriptor:ClassConstructorDescriptor, args:MutableList ):LLVMOpaqueValue? { + val tmp = generator.load(generator.thisVariable(), generator.tmpVariable()) + var rargs:MutableList? = null + if (args.size != 0) + rargs = mutableListOf(tmp, *args.toTypedArray()) + else + rargs = mutableListOf(tmp) + return callDirect(descriptor, rargs, result) + } + }