[K/N][codegen] Trampoline to call virtual functions to reduce dependencies coupling
This commit is contained in:
+2
@@ -99,6 +99,8 @@ internal class NativeGenerationState(
|
||||
val cStubsManager = CStubsManager(config.target, this)
|
||||
lateinit var llvmDeclarations: LlvmDeclarations
|
||||
|
||||
val virtualFunctionTrampolines = mutableMapOf<IrSimpleFunction, LlvmCallable>()
|
||||
|
||||
val coverage by lazy { CoverageManager(this) }
|
||||
|
||||
lateinit var objCExport: ObjCExport
|
||||
|
||||
+3
-5
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.isOverridable
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
|
||||
@@ -49,11 +50,8 @@ internal class CAdapterCodegen(
|
||||
val bridgeFunctionProto = signature.toProto(cname, null, LLVMLinkage.LLVMExternalLinkage)
|
||||
// If function is virtual, we need to resolve receiver properly.
|
||||
generateFunction(codegen, bridgeFunctionProto) {
|
||||
val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
|
||||
irFunction.isOverridable
|
||||
) {
|
||||
val receiver = param(0)
|
||||
lookupVirtualImpl(receiver, irFunction)
|
||||
val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) && irFunction.isOverridable) {
|
||||
codegen.getVirtualFunctionTrampoline(irFunction as IrSimpleFunction)
|
||||
} else {
|
||||
// KT-45468: Alias insertion may not be handled by LLVM properly, in case callee is in the cache.
|
||||
// Hence, insert not an alias but a wrapper, hoping it will be optimized out later.
|
||||
|
||||
+145
-96
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.ClassGlobalHierarchyInfo
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.KonanBinaryInterface.symbolName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Native
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.*
|
||||
@@ -116,6 +117,7 @@ internal inline fun generateFunction(
|
||||
startLocation,
|
||||
endLocation,
|
||||
switchToRunnable = isCToKotlinBridge,
|
||||
needSafePoint = true,
|
||||
function)
|
||||
|
||||
if (isCToKotlinBridge) {
|
||||
@@ -147,6 +149,7 @@ internal inline fun generateFunction(
|
||||
startLocation: LocationInfo? = null,
|
||||
endLocation: LocationInfo? = null,
|
||||
switchToRunnable: Boolean = false,
|
||||
needSafePoint: Boolean = true,
|
||||
code: FunctionGenerationContext.() -> Unit
|
||||
) : LlvmCallable {
|
||||
val function = codegen.addFunction(functionProto)
|
||||
@@ -155,7 +158,8 @@ internal inline fun generateFunction(
|
||||
codegen,
|
||||
startLocation,
|
||||
endLocation,
|
||||
switchToRunnable = switchToRunnable
|
||||
switchToRunnable = switchToRunnable,
|
||||
needSafePoint = needSafePoint
|
||||
)
|
||||
try {
|
||||
generateFunctionBody(functionGenerationContext, code)
|
||||
@@ -172,7 +176,8 @@ internal inline fun generateFunctionNoRuntime(
|
||||
code: FunctionGenerationContext.() -> Unit,
|
||||
) : LlvmCallable {
|
||||
val function = codegen.addFunction(functionProto)
|
||||
val functionGenerationContext = DefaultFunctionGenerationContext(function, codegen, null, null, switchToRunnable = false)
|
||||
val functionGenerationContext = DefaultFunctionGenerationContext(function, codegen, null, null,
|
||||
switchToRunnable = false, needSafePoint = true)
|
||||
try {
|
||||
functionGenerationContext.forbidRuntime = true
|
||||
require(!functionGenerationContext.isObjectType(functionGenerationContext.returnType!!)) {
|
||||
@@ -197,6 +202,134 @@ private inline fun <T : FunctionGenerationContext> generateFunctionBody(
|
||||
functionGenerationContext.resetDebugLocation()
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
|
||||
if (modality == Modality.ABSTRACT) return null
|
||||
val resolved = resolveFakeOverride()!!
|
||||
if ((resolved.parent as IrClass).isAny()) {
|
||||
return resolved
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
internal object VirtualTablesLookup {
|
||||
fun FunctionGenerationContext.getInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef {
|
||||
val interfaceTableSize = load(structGep(typeInfo, 9 /* interfaceTableSize_ */))
|
||||
val interfaceTable = load(structGep(typeInfo, 10 /* interfaceTable_ */))
|
||||
|
||||
fun fastPath(): LLVMValueRef {
|
||||
// The fastest optimistic version.
|
||||
val interfaceTableIndex = and(interfaceTableSize, llvm.int32(interfaceId))
|
||||
return gep(interfaceTable, interfaceTableIndex)
|
||||
}
|
||||
|
||||
// See details in ClassLayoutBuilder.
|
||||
return if (context.ghaEnabled()
|
||||
&& context.globalHierarchyAnalysisResult.bitsPerColor <= ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
|
||||
&& context.config.produce != CompilerOutputKind.FRAMEWORK
|
||||
) {
|
||||
// All interface tables are small and no unknown interface inheritance.
|
||||
fastPath()
|
||||
} else {
|
||||
val startLocationInfo = position()?.start
|
||||
val fastPathBB = basicBlock("fast_path", startLocationInfo)
|
||||
val slowPathBB = basicBlock("slow_path", startLocationInfo)
|
||||
val takeResBB = basicBlock("take_res", startLocationInfo)
|
||||
condBr(icmpGe(interfaceTableSize, llvm.kImmInt32Zero), fastPathBB, slowPathBB)
|
||||
positionAtEnd(takeResBB)
|
||||
val resultPhi = phi(pointerType(runtime.interfaceTableRecordType))
|
||||
appendingTo(fastPathBB) {
|
||||
val fastValue = fastPath()
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to fastValue)
|
||||
}
|
||||
appendingTo(slowPathBB) {
|
||||
val actualInterfaceTableSize = sub(llvm.kImmInt32Zero, interfaceTableSize) // -interfaceTableSize
|
||||
val slowValue = call(llvm.lookupInterfaceTableRecord,
|
||||
listOf(interfaceTable, actualInterfaceTableSize, llvm.int32(interfaceId)))
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to slowValue)
|
||||
}
|
||||
resultPhi
|
||||
}
|
||||
}
|
||||
|
||||
fun FunctionGenerationContext.getVirtualImpl(receiver: LLVMValueRef, irFunction: IrSimpleFunction): LlvmCallable {
|
||||
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
|
||||
|
||||
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null)
|
||||
call(llvm.getObjCKotlinTypeInfo, listOf(receiver))
|
||||
else
|
||||
loadTypeInfo(receiver)
|
||||
|
||||
assert(typeInfoPtr.type == codegen.kTypeInfoPtr) { llvmtype2string(typeInfoPtr.type) }
|
||||
|
||||
val owner = irFunction.parentAsClass
|
||||
val canCallViaVtable = !owner.isInterface
|
||||
val layoutBuilder = generationState.context.getLayoutBuilder(owner)
|
||||
|
||||
val llvmMethod = when {
|
||||
canCallViaVtable -> {
|
||||
val index = layoutBuilder.vtableIndex(irFunction)
|
||||
val vtablePlace = gep(typeInfoPtr, llvm.int32(1)) // typeInfoPtr + 1
|
||||
val vtable = bitcast(llvm.int8PtrPtrType, vtablePlace)
|
||||
val slot = gep(vtable, llvm.int32(index))
|
||||
load(slot)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Essentially: typeInfo.itable[place(interfaceId)].vtable[method]
|
||||
val itablePlace = layoutBuilder.itablePlace(irFunction)
|
||||
val interfaceTableRecord = getInterfaceTableRecord(typeInfoPtr, itablePlace.interfaceId)
|
||||
load(gep(load(structGep(interfaceTableRecord, 2 /* vtable */)), llvm.int32(itablePlace.methodIndex)))
|
||||
}
|
||||
}
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction))
|
||||
return LlvmCallable(
|
||||
bitcast(functionPtrType, llvmMethod),
|
||||
LlvmFunctionSignature(irFunction, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Special trampoline function to call actual virtual implementation. This helps with reducing
|
||||
* dependence between klibs/files (if vtable/itable of some class has changed, the call sites
|
||||
* would be the same and wouldn't need recompiling).
|
||||
*/
|
||||
internal fun CodeGenerator.getVirtualFunctionTrampoline(irFunction: IrSimpleFunction): LlvmCallable {
|
||||
/*
|
||||
* Resolve owner of the call with special handling of Any methods:
|
||||
* if toString/eq/hc is invoked on an interface instance, we resolve
|
||||
* owner as Any and dispatch it via vtable.
|
||||
*/
|
||||
val anyMethod = irFunction.findOverriddenMethodOfAny()
|
||||
return getVirtualFunctionTrampolineImpl(anyMethod ?: irFunction)
|
||||
}
|
||||
|
||||
private fun CodeGenerator.getVirtualFunctionTrampolineImpl(irFunction: IrSimpleFunction) =
|
||||
generationState.virtualFunctionTrampolines.getOrPut(irFunction) {
|
||||
val targetName = if (irFunction.isExported())
|
||||
irFunction.symbolName
|
||||
else
|
||||
irFunction.computePrivateSymbolName(irFunction.parentAsClass.fqNameForIrSerialization.asString())
|
||||
val proto = LlvmFunctionProto(
|
||||
name = "$targetName-trampoline",
|
||||
signature = LlvmFunctionSignature(irFunction, this),
|
||||
origin = null,
|
||||
linkage = linkageOf(irFunction)
|
||||
)
|
||||
if (isExternal(irFunction))
|
||||
llvm.externalFunction(proto)
|
||||
else generateFunction(this, proto, needSafePoint = false) {
|
||||
val args = proto.signature.parameterTypes.indices.map { param(it) }
|
||||
val receiver = param(0)
|
||||
val callee = with(VirtualTablesLookup) { getVirtualImpl(receiver, irFunction) }
|
||||
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
|
||||
ret(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There're cases when we don't need end position or it is meaningless.
|
||||
*/
|
||||
@@ -390,6 +523,7 @@ internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationC
|
||||
var startLocation: LocationInfo? = null
|
||||
var endLocation: LocationInfo? = null
|
||||
var switchToRunnable = false
|
||||
var needSafePoint = true
|
||||
var irFunction: IrFunction? = null
|
||||
|
||||
abstract fun build(): T
|
||||
@@ -401,6 +535,7 @@ internal abstract class FunctionGenerationContext(
|
||||
private val startLocation: LocationInfo?,
|
||||
protected val endLocation: LocationInfo?,
|
||||
switchToRunnable: Boolean,
|
||||
needSafePoint: Boolean,
|
||||
internal val irFunction: IrFunction? = null
|
||||
) : ContextUtils {
|
||||
|
||||
@@ -410,6 +545,7 @@ internal abstract class FunctionGenerationContext(
|
||||
startLocation = builder.startLocation,
|
||||
endLocation = builder.endLocation,
|
||||
switchToRunnable = builder.switchToRunnable,
|
||||
needSafePoint = builder.needSafePoint,
|
||||
irFunction = builder.irFunction
|
||||
)
|
||||
|
||||
@@ -455,6 +591,9 @@ internal abstract class FunctionGenerationContext(
|
||||
private val switchToRunnable: Boolean =
|
||||
context.memoryModel == MemoryModel.EXPERIMENTAL && switchToRunnable
|
||||
|
||||
private val needSafePoint: Boolean =
|
||||
context.memoryModel == MemoryModel.EXPERIMENTAL && needSafePoint
|
||||
|
||||
val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb)
|
||||
|
||||
data class FunctionInvokeInformation(
|
||||
@@ -1112,98 +1251,6 @@ internal abstract class FunctionGenerationContext(
|
||||
return load(typeInfoPtrPtr, memoryOrder = LLVMAtomicOrdering.LLVMAtomicOrderingMonotonic)
|
||||
}
|
||||
|
||||
fun lookupInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef {
|
||||
val interfaceTableSize = load(structGep(typeInfo, 9 /* interfaceTableSize_ */))
|
||||
val interfaceTable = load(structGep(typeInfo, 10 /* interfaceTable_ */))
|
||||
|
||||
fun fastPath(): LLVMValueRef {
|
||||
// The fastest optimistic version.
|
||||
val interfaceTableIndex = and(interfaceTableSize, llvm.int32(interfaceId))
|
||||
return gep(interfaceTable, interfaceTableIndex)
|
||||
}
|
||||
|
||||
// See details in ClassLayoutBuilder.
|
||||
return if (context.ghaEnabled()
|
||||
&& context.globalHierarchyAnalysisResult.bitsPerColor <= ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
|
||||
&& context.config.produce != CompilerOutputKind.FRAMEWORK) {
|
||||
// All interface tables are small and no unknown interface inheritance.
|
||||
fastPath()
|
||||
} else {
|
||||
val startLocationInfo = position()?.start
|
||||
val fastPathBB = basicBlock("fast_path", startLocationInfo)
|
||||
val slowPathBB = basicBlock("slow_path", startLocationInfo)
|
||||
val takeResBB = basicBlock("take_res", startLocationInfo)
|
||||
condBr(icmpGe(interfaceTableSize, llvm.kImmInt32Zero), fastPathBB, slowPathBB)
|
||||
positionAtEnd(takeResBB)
|
||||
val resultPhi = phi(pointerType(runtime.interfaceTableRecordType))
|
||||
appendingTo(fastPathBB) {
|
||||
val fastValue = fastPath()
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to fastValue)
|
||||
}
|
||||
appendingTo(slowPathBB) {
|
||||
val actualInterfaceTableSize = sub(llvm.kImmInt32Zero, interfaceTableSize) // -interfaceTableSize
|
||||
val slowValue = call(llvm.lookupInterfaceTableRecord,
|
||||
listOf(interfaceTable, actualInterfaceTableSize, llvm.int32(interfaceId)))
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to slowValue)
|
||||
}
|
||||
resultPhi
|
||||
}
|
||||
}
|
||||
|
||||
fun lookupVirtualImpl(receiver: LLVMValueRef, irFunction: IrFunction): LlvmCallable {
|
||||
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
|
||||
|
||||
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null)
|
||||
call(llvm.getObjCKotlinTypeInfo, listOf(receiver))
|
||||
else
|
||||
loadTypeInfo(receiver)
|
||||
|
||||
assert(typeInfoPtr.type == codegen.kTypeInfoPtr) { LLVMPrintTypeToString(typeInfoPtr.type)!!.toKString() }
|
||||
|
||||
/*
|
||||
* Resolve owner of the call with special handling of Any methods:
|
||||
* if toString/eq/hc is invoked on an interface instance, we resolve
|
||||
* owner as Any and dispatch it via vtable.
|
||||
*/
|
||||
val anyMethod = (irFunction as IrSimpleFunction).findOverriddenMethodOfAny()
|
||||
val owner = (anyMethod ?: irFunction).parentAsClass
|
||||
|
||||
val llvmMethod = when {
|
||||
!owner.isInterface -> {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction)
|
||||
val vtablePlace = gep(typeInfoPtr, llvm.int32(1)) // typeInfoPtr + 1
|
||||
val vtable = bitcast(llvm.int8PtrPtrType, vtablePlace)
|
||||
val slot = gep(vtable, llvm.int32(index))
|
||||
load(slot)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Essentially: typeInfo.itable[place(interfaceId)].vtable[method]
|
||||
val itablePlace = context.getLayoutBuilder(owner).itablePlace(irFunction)
|
||||
val interfaceTableRecord = lookupInterfaceTableRecord(typeInfoPtr, itablePlace.interfaceId)
|
||||
load(gep(load(structGep(interfaceTableRecord, 2 /* vtable */)), llvm.int32(itablePlace.methodIndex)))
|
||||
}
|
||||
}
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction))
|
||||
return LlvmCallable(
|
||||
bitcast(functionPtrType, llvmMethod),
|
||||
LlvmFunctionSignature(irFunction, this)
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
|
||||
if (modality == Modality.ABSTRACT) return null
|
||||
val resolved = resolveFakeOverride()!!
|
||||
if ((resolved.parent as IrClass).isAny()) {
|
||||
return resolved
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getObjectValue(irClass: IrClass, exceptionHandler: ExceptionHandler,
|
||||
startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = null,
|
||||
@@ -1279,7 +1326,7 @@ internal abstract class FunctionGenerationContext(
|
||||
currentPositionHolder.resetBuilderDebugLocation()
|
||||
}
|
||||
|
||||
private fun position() = basicBlockToLastLocation[currentBlock]
|
||||
fun position() = basicBlockToLastLocation[currentBlock]
|
||||
|
||||
internal fun mapParameterForDebug(index: Int, value: LLVMValueRef) {
|
||||
appendingTo(localsInitBb) {
|
||||
@@ -1366,7 +1413,7 @@ internal abstract class FunctionGenerationContext(
|
||||
} else {
|
||||
check(!setCurrentFrameIsCalled)
|
||||
}
|
||||
if (context.memoryModel == MemoryModel.EXPERIMENTAL && !forbidRuntime) {
|
||||
if (context.memoryModel == MemoryModel.EXPERIMENTAL && !forbidRuntime && needSafePoint) {
|
||||
call(llvm.Kotlin_mm_safePointFunctionPrologue, emptyList())
|
||||
}
|
||||
resetDebugLocation()
|
||||
@@ -1572,6 +1619,7 @@ internal class DefaultFunctionGenerationContext(
|
||||
startLocation: LocationInfo?,
|
||||
endLocation: LocationInfo?,
|
||||
switchToRunnable: Boolean,
|
||||
needSafePoint: Boolean,
|
||||
irFunction: IrFunction? = null
|
||||
) : FunctionGenerationContext(
|
||||
function,
|
||||
@@ -1579,6 +1627,7 @@ internal class DefaultFunctionGenerationContext(
|
||||
startLocation,
|
||||
endLocation,
|
||||
switchToRunnable,
|
||||
needSafePoint,
|
||||
irFunction
|
||||
) {
|
||||
// Note: return handling can be extracted to a separate class.
|
||||
|
||||
+7
@@ -162,6 +162,13 @@ internal interface ContextUtils : RuntimeAware {
|
||||
return !generationState.llvmModuleSpecification.containsDeclaration(declaration)
|
||||
}
|
||||
|
||||
fun linkageOf(irFunction: IrFunction) = when {
|
||||
isExternal(irFunction) -> LLVMLinkage.LLVMExternalLinkage
|
||||
irFunction.isExported() -> LLVMLinkage.LLVMExternalLinkage
|
||||
context.config.producePerFileCache && irFunction in generationState.calledFromExportedInlineFunctions -> LLVMLinkage.LLVMExternalLinkage
|
||||
else -> LLVMLinkage.LLVMInternalLinkage
|
||||
}
|
||||
|
||||
/**
|
||||
* LLVM function generated from the Kotlin function.
|
||||
* It may be declared as external function prototype.
|
||||
|
||||
+11
-3
@@ -784,9 +784,16 @@ internal class CodeGeneratorVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildVirtualFunctionTrampoline(irFunction: IrSimpleFunction) {
|
||||
codegen.getVirtualFunctionTrampoline(irFunction)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
context.log{"visitFunction : ${ir2string(declaration)}"}
|
||||
|
||||
if (declaration is IrSimpleFunction && declaration.isOverridable && declaration.origin !is DECLARATION_ORIGIN_BRIDGE_METHOD)
|
||||
buildVirtualFunctionTrampoline(declaration)
|
||||
|
||||
val scopeState = llvm.initializersGenerationState.scopeState
|
||||
if (declaration.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER) {
|
||||
require(scopeState.globalInitFunction == null) { "There can only be at most one global file initializer" }
|
||||
@@ -1597,7 +1604,8 @@ internal class CodeGeneratorVisitor(
|
||||
val interfaceId = dstHierarchyInfo.interfaceId
|
||||
val typeInfo = functionGenerationContext.loadTypeInfo(srcObjInfoPtr)
|
||||
with(functionGenerationContext) {
|
||||
val interfaceTableRecord = lookupInterfaceTableRecord(typeInfo, interfaceId)
|
||||
|
||||
val interfaceTableRecord = with(VirtualTablesLookup) { getInterfaceTableRecord(typeInfo, interfaceId) }
|
||||
icmpEq(load(structGep(interfaceTableRecord, 0 /* id */)), llvm.int32(interfaceId))
|
||||
}
|
||||
}
|
||||
@@ -2617,8 +2625,8 @@ internal class CodeGeneratorVisitor(
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun callVirtual(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
val functionDeclarations = functionGenerationContext.lookupVirtualImpl(args.first(), function)
|
||||
fun callVirtual(function: IrSimpleFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, resultSlot: LLVMValueRef?): LLVMValueRef {
|
||||
val functionDeclarations = codegen.getVirtualFunctionTrampoline(function)
|
||||
return call(function, functionDeclarations, args, resultLifetime, resultSlot)
|
||||
}
|
||||
|
||||
|
||||
+1
-6
@@ -392,12 +392,7 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
|
||||
}
|
||||
}
|
||||
|
||||
val linkage = when {
|
||||
declaration.isExported() -> LLVMLinkage.LLVMExternalLinkage
|
||||
context.config.producePerFileCache && declaration in generationState.calledFromExportedInlineFunctions -> LLVMLinkage.LLVMExternalLinkage
|
||||
else -> LLVMLinkage.LLVMInternalLinkage
|
||||
}
|
||||
val proto = LlvmFunctionProto(declaration, symbolName, this, linkage)
|
||||
val proto = LlvmFunctionProto(declaration, symbolName, this, linkageOf(declaration))
|
||||
context.log {
|
||||
"Creating llvm function ${symbolName} for ${declaration.render()}"
|
||||
}
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
|
||||
|
||||
val invokeMethod = context.ir.symbols.functionN(numberOfParameters).owner.simpleFunctions()
|
||||
.single { it.name == OperatorNameConventions.INVOKE }
|
||||
val llvmDeclarations = lookupVirtualImpl(kotlinFunction, invokeMethod)
|
||||
val llvmDeclarations = codegen.getVirtualFunctionTrampoline(invokeMethod)
|
||||
val result = callFromBridge(llvmDeclarations, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT)
|
||||
if (bridge.returnsVoid) {
|
||||
ret(null)
|
||||
|
||||
+3
-3
@@ -945,10 +945,10 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
||||
listOf(param(0), codegen.typeInfoValue(target.parent as IrClass))
|
||||
)
|
||||
}
|
||||
val llvmCallable = if (!isVirtual) {
|
||||
codegen.llvmFunction(target)
|
||||
val llvmCallable = if (isVirtual) {
|
||||
codegen.getVirtualFunctionTrampoline(target as IrSimpleFunction)
|
||||
} else {
|
||||
lookupVirtualImpl(args.first(), target)
|
||||
codegen.llvmFunction(target)
|
||||
}
|
||||
call(llvmCallable, args, resultLifetime, exceptionHandler)
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ func testDirectObjc2Kotlin() throws {
|
||||
|
||||
func testVirtualObjc2Kotlin() throws {
|
||||
let trace = StacktraceBridgesKt.createBar().foo()
|
||||
try assertTrue(trace[5].contains("objc2kotlin_virtual_kfun:Foo#foo(){}kotlin.collections.List<kotlin.String>"))
|
||||
try assertTrue(trace[6].contains("objc2kotlin_virtual_kfun:Foo#foo(){}kotlin.collections.List<kotlin.String>"))
|
||||
}
|
||||
|
||||
func testKotlin2Objc() throws {
|
||||
|
||||
+4
-3
@@ -6,9 +6,10 @@
|
||||
frame #1: [..]`kfun:[..]main$lambda$0[..] at kt42208-2.kt:10:18
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0[..]invoke[..](_this=[..])[..] at kt42208-1.kt:7:60
|
||||
frame #3: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]$<bridge-UNN>invoke(_this=[..]){}kotlin.Nothing[..] at kt42208-1.kt:7:60
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:5:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #6: [..]
|
||||
frame #4: [..]`kfun:kotlin.Function0#invoke(){}1:0-trampoline + 73
|
||||
frame #5: [..]`kfun:#main(){} at kt42208-1.kt:5:5
|
||||
frame #6: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #7: [..]
|
||||
frame #8: [..]
|
||||
> q
|
||||
|
||||
|
||||
+4
-3
@@ -5,8 +5,9 @@
|
||||
* frame #0: [..]`kfun:[..]main$lambda$0[..] at kt42208-2.kt:14:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]invoke[..](_this=[..])[..] at kt42208-1.kt:9:82
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean[..] at kt42208-1.kt:9:82
|
||||
frame #3: [..]`kfun:#bar(v=[..]){} at kt42208-3.kt:18:5
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #3: [..]`kfun:kotlin.Function0#invoke(){}1:0-trampoline + 73
|
||||
frame #4: [..]`kfun:#bar(v=[..]){} at kt42208-3.kt:18:5
|
||||
frame #5: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #6: [..]`Konan_start(args=[..]) at [..]
|
||||
> c
|
||||
> q
|
||||
|
||||
+10
-7
@@ -5,23 +5,26 @@
|
||||
* frame #0: [..]`kfun:[..]main$lambda$0[..] at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0[..]invoke[..](_this=[..])[..] at kt42208-1.kt:10:71
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean[..] at kt42208-1.kt:10:71
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:6:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #5: [..]
|
||||
frame #3: [..]`kfun:kotlin.Function0#invoke(){}1:0-trampoline + 73
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:6:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #6: [..]
|
||||
> c
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:[..]main$lambda$0[..] at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0[..]invoke[..](_this=[..])[..] at kt42208-1.kt:10:71
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean[..] at kt42208-1.kt:10:71
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #3: [..]`kfun:kotlin.Function0#invoke(){}1:0-trampoline + 73
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at [..]
|
||||
> c
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:[..]main$lambda$0[..] at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0[..]invoke[..](_this=[..])[..] at kt42208-1.kt:10:71
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.[..]$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean[..] at kt42208-1.kt:10:71
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:8:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at [..]
|
||||
frame #3: [..]`kfun:kotlin.Function0#invoke(){}1:0-trampoline + 73
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:8:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at [..]
|
||||
> q
|
||||
|
||||
Reference in New Issue
Block a user