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<String>) {
  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<-----------------
This commit is contained in:
Konstantin Anisimov
2016-11-10 18:51:11 +03:00
committed by vvlevchenko
parent 24e0ae7d00
commit c47a58d34f
3 changed files with 65 additions and 17 deletions
@@ -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<LLVMOpaqueValue?>, result: String?): LLVMOpaqueValue? {
if (args.size == 0) return LLVMBuildCall(context.llvmBuilder, descriptor.llvmFunction.getLlvmValue(), null, 0, result)
//-------------------------------------------------------------------------//
fun call(llvmFunction: LLVMOpaqueValue?, args: MutableList<LLVMOpaqueValue?>, 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?> ):LLVMOpaqueValue? {
val tmp = load(thisVariable(), tmpVariable())
var rargs:MutableList<LLVMOpaqueValue?>? = null
if (args.size != 0)
rargs = mutableListOf<LLVMOpaqueValue?>(tmp, *args.toTypedArray())
else
rargs = mutableListOf<LLVMOpaqueValue?>(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()
}
@@ -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)
@@ -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?>): 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<LLVMOpaqueValue?>, 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<LLVMOpaqueValue?>, 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?> ):LLVMOpaqueValue? {
val tmp = generator.load(generator.thisVariable(), generator.tmpVariable())
var rargs:MutableList<LLVMOpaqueValue?>? = null
if (args.size != 0)
rargs = mutableListOf<LLVMOpaqueValue?>(tmp, *args.toTypedArray())
else
rargs = mutableListOf<LLVMOpaqueValue?>(tmp)
return callDirect(descriptor, rargs, result)
}
}