[K/N] refactor all function calls to use LlvmCallable machinery

This commit is contained in:
Pavel Kunyavskiy
2023-02-23 12:28:28 +01:00
committed by Space Team
parent ff3fac5d42
commit 854506fa9e
20 changed files with 417 additions and 437 deletions
@@ -55,7 +55,11 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions
listOf(LlvmParamType(llvm.int8PtrType)) listOf(LlvmParamType(llvm.int8PtrType))
) )
val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall") val checkerFunction = llvm.externalNativeRuntimeFunction(
"Kotlin_mm_checkStateAtExternalFunctionCall",
LlvmRetType(llvm.voidType),
listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType))
)
private data class ExternalCallInfo(val name: String?, val calledPtr: LLVMValueRef) private data class ExternalCallInfo(val name: String?, val calledPtr: LLVMValueRef)
@@ -93,7 +97,7 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions
val calls = getInstructions(block) val calls = getInstructions(block)
.filter { it.isFunctionCall() } .filter { it.isFunctionCall() }
.toList() .toList()
val builder = LLVMCreateBuilderInContext(llvm.llvmContext) val builder = LLVMCreateBuilderInContext(llvm.llvmContext)!!
for (call in calls) { for (call in calls) {
val calleeInfo = call.getPossiblyExternalCalledFunction() ?: continue val calleeInfo = call.getPossiblyExternalCalledFunction() ?: continue
@@ -102,21 +106,21 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions
LLVMBuilderResetDebugLocation(builder) LLVMBuilderResetDebugLocation(builder)
val callSiteDescription: String val callSiteDescription: String
val calledName: String? val calledName: String?
val calledPtrLlvm: LLVMValueRef? val calledPtrLlvm: LLVMValueRef
when (calleeInfo.name) { when (calleeInfo.name) {
"objc_msgSend" -> { "objc_msgSend" -> {
// objc_msgSend has wrong declaration in header, so generated wrapper is strange, Let's just skip it // objc_msgSend has wrong declaration in header, so generated wrapper is strange, Let's just skip it
if (LLVMGetNumArgOperands(call) < 2) continue if (LLVMGetNumArgOperands(call) < 2) continue
callSiteDescription = "$functionName (over objc_msgSend)" callSiteDescription = "$functionName (over objc_msgSend)"
calledName = null calledName = null
val firstArgI8Ptr = LLVMBuildBitCast(builder, LLVMGetArgOperand(call, 0), llvm.int8PtrType, "") val firstArgI8Ptr = LLVMBuildBitCast(builder, LLVMGetArgOperand(call, 0), llvm.int8PtrType, "")!!
val firstArgClassPtr = LLVMBuildCall(builder, getClass.llvmValue, listOf(firstArgI8Ptr).toCValues(), 1, "") val firstArgClassPtr = getClass.buildCall(builder, listOf(firstArgI8Ptr))
val isNil = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, firstArgI8Ptr, LLVMConstNull(llvm.int8PtrType), "") val isNil = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, firstArgI8Ptr, LLVMConstNull(llvm.int8PtrType), "")
val selector = LLVMGetArgOperand(call, 1) val selector = LLVMGetArgOperand(call, 1)!!
val calledPtrLlvmIfNotNilFunPtr = LLVMBuildCall(builder, getMethodImpl.llvmValue, listOf(firstArgClassPtr, selector).toCValues(), 2, "") val calledPtrLlvmIfNotNilFunPtr = getMethodImpl.buildCall(builder, listOf(firstArgClassPtr, selector))
val calledPtrLlvmIfNotNil = LLVMBuildBitCast(builder, calledPtrLlvmIfNotNilFunPtr, llvm.int8PtrType, "") val calledPtrLlvmIfNotNil = LLVMBuildBitCast(builder, calledPtrLlvmIfNotNilFunPtr, llvm.int8PtrType, "")
val calledPtrLlvmIfNil = LLVMConstIntToPtr(llvm.int64(MSG_SEND_TO_NULL), llvm.int8PtrType) val calledPtrLlvmIfNil = LLVMConstIntToPtr(llvm.int64(MSG_SEND_TO_NULL), llvm.int8PtrType)
calledPtrLlvm = LLVMBuildSelect(builder, isNil, calledPtrLlvmIfNil, calledPtrLlvmIfNotNil, "") calledPtrLlvm = LLVMBuildSelect(builder, isNil, calledPtrLlvmIfNil, calledPtrLlvmIfNotNil, "")!!
} }
"objc_msgSendSuper2" -> { "objc_msgSendSuper2" -> {
if (LLVMGetNumArgOperands(call) < 2) continue if (LLVMGetNumArgOperands(call) < 2) continue
@@ -124,30 +128,30 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions
calledName = null calledName = null
val superStruct = LLVMGetArgOperand(call, 0) val superStruct = LLVMGetArgOperand(call, 0)
val superClassPtrPtr = LLVMBuildGEP(builder, superStruct, listOf(llvm.int32(0), llvm.int32(1)).toCValues(), 2, "") val superClassPtrPtr = LLVMBuildGEP(builder, superStruct, listOf(llvm.int32(0), llvm.int32(1)).toCValues(), 2, "")
val superClassPtr = LLVMBuildLoad(builder, superClassPtrPtr, "") val superClassPtr = LLVMBuildLoad(builder, superClassPtrPtr, "")!!
val classPtr = LLVMBuildCall(builder, getSuperClass.llvmValue, listOf(superClassPtr).toCValues(), 1, "") val classPtr = getSuperClass.buildCall(builder, listOf(superClassPtr))
val calledPtrLlvmFunPtr = LLVMBuildCall(builder, getMethodImpl.llvmValue, listOf(classPtr, LLVMGetArgOperand(call, 1)).toCValues(), 2, "") val calledPtrLlvmFunPtr = getMethodImpl.buildCall(builder, listOf(classPtr, LLVMGetArgOperand(call, 1)!!))
calledPtrLlvm = LLVMBuildBitCast(builder, calledPtrLlvmFunPtr, llvm.int8PtrType, "") calledPtrLlvm = LLVMBuildBitCast(builder, calledPtrLlvmFunPtr, llvm.int8PtrType, "")!!
} }
else -> { else -> {
callSiteDescription = functionName callSiteDescription = functionName
calledName = calleeInfo.name calledName = calleeInfo.name
calledPtrLlvm = when (val typeKind = LLVMGetTypeKind(calleeInfo.calledPtr.type)) { calledPtrLlvm = when (val typeKind = LLVMGetTypeKind(calleeInfo.calledPtr.type)) {
LLVMTypeKind.LLVMPointerTypeKind -> LLVMBuildBitCast(builder, calleeInfo.calledPtr, llvm.int8PtrType, "") LLVMTypeKind.LLVMPointerTypeKind -> LLVMBuildBitCast(builder, calleeInfo.calledPtr, llvm.int8PtrType, "")!!
LLVMTypeKind.LLVMIntegerTypeKind -> LLVMBuildIntToPtr(builder, calleeInfo.calledPtr, llvm.int8PtrType, "") LLVMTypeKind.LLVMIntegerTypeKind -> LLVMBuildIntToPtr(builder, calleeInfo.calledPtr, llvm.int8PtrType, "")!!
else -> TODO("Unsupported typeKind=${typeKind} of calledPtr=${llvm2string(calleeInfo.calledPtr)}") else -> TODO("Unsupported typeKind=${typeKind} of calledPtr=${llvm2string(calleeInfo.calledPtr)}")
} }
} }
} }
val callSiteDescriptionLlvm = llvm.staticData.cStringLiteral(callSiteDescription).llvm val callSiteDescriptionLlvm = llvm.staticData.cStringLiteral(callSiteDescription).llvm
val calledNameLlvm = if (calledName == null) LLVMConstNull(llvm.int8PtrType) else llvm.staticData.cStringLiteral(calledName).llvm val calledNameLlvm = if (calledName == null) LLVMConstNull(llvm.int8PtrType)!! else llvm.staticData.cStringLiteral(calledName).llvm
LLVMBuildCall(builder, checkerFunction, listOf(callSiteDescriptionLlvm, calledNameLlvm, calledPtrLlvm).toCValues(), 3, "") checkerFunction.buildCall(builder, listOf(callSiteDescriptionLlvm, calledNameLlvm, calledPtrLlvm))
} }
LLVMDisposeBuilder(builder) LLVMDisposeBuilder(builder)
} }
fun processFunction(function: LLVMValueRef) { fun processFunction(function: LLVMValueRef) {
if (function == checkerFunction) return if (function.name == checkerFunction.name) return
getBasicBlocks(function).forEach { getBasicBlocks(function).forEach {
processBasicBlock(function.name!!, it) processBasicBlock(function.name!!, it)
} }
@@ -180,8 +184,8 @@ internal fun checkLlvmModuleExternalCalls(generationState: NativeGenerationState
.filter { !it.isExternalFunction() && it !in ignoredFunctions } .filter { !it.isExternalFunction() && it !in ignoredFunctions }
.forEach(checker::processFunction) .forEach(checker::processFunction)
// otherwise optimiser can inline it // otherwise optimiser can inline it
staticData.getGlobal(functionListGlobal)?.setExternallyInitialized(true); staticData.getGlobal(functionListGlobal)?.setExternallyInitialized(true)
staticData.getGlobal(functionListSizeGlobal)?.setExternallyInitialized(true); staticData.getGlobal(functionListSizeGlobal)?.setExternallyInitialized(true)
verifyModule(llvm.module) verifyModule(llvm.module)
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.konan.cexport package org.jetbrains.kotlin.backend.konan.cexport
import kotlinx.cinterop.cValuesOf
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
@@ -13,8 +12,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
import org.jetbrains.kotlin.backend.konan.llvm.ContextUtils import org.jetbrains.kotlin.backend.konan.llvm.ContextUtils
import org.jetbrains.kotlin.backend.konan.llvm.ExceptionHandler import org.jetbrains.kotlin.backend.konan.llvm.ExceptionHandler
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
import org.jetbrains.kotlin.backend.konan.llvm.addLlvmFunctionWithDefaultAttributes
import org.jetbrains.kotlin.backend.konan.llvm.generateFunction
import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -48,9 +45,10 @@ internal class CAdapterCodegen(
val function = declaration as FunctionDescriptor val function = declaration as FunctionDescriptor
val irFunction = irSymbol.owner as IrFunction val irFunction = irSymbol.owner as IrFunction
cname = "_konan_function_${owner.nextFunctionIndex()}" cname = "_konan_function_${owner.nextFunctionIndex()}"
val llvmCallable = codegen.llvmFunction(irFunction) val signature = LlvmFunctionSignature(irFunction, this@CAdapterCodegen)
val bridgeFunctionProto = signature.toProto(cname, null, LLVMLinkage.LLVMExternalLinkage)
// If function is virtual, we need to resolve receiver properly. // If function is virtual, we need to resolve receiver properly.
val bridge = generateFunction(codegen, llvmCallable.functionType, cname) { generateFunction(codegen, bridgeFunctionProto) {
val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) && val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
irFunction.isOverridable irFunction.isOverridable
) { ) {
@@ -59,35 +57,36 @@ internal class CAdapterCodegen(
} else { } else {
// KT-45468: Alias insertion may not be handled by LLVM properly, in case callee is in the cache. // 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. // Hence, insert not an alias but a wrapper, hoping it will be optimized out later.
llvmCallable codegen.llvmFunction(irFunction)
} }
val numParams = LLVMCountParams(llvmCallable.llvmValue) val args = signature.parameterTypes.indices.map { param(it) }
val args = (0 until numParams).map { index -> param(index) }
callee.attributeProvider.addFunctionAttributes(this.function)
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true) val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result) ret(result)
} }
LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage)
} }
isClass -> { isClass -> {
val irClass = irSymbol.owner as IrClass val irClass = irSymbol.owner as IrClass
cname = "_konan_function_${owner.nextFunctionIndex()}" cname = "_konan_function_${owner.nextFunctionIndex()}"
// Produce type getter. // Produce type getter.
val getTypeFunction = addLlvmFunctionWithDefaultAttributes( val getTypeFunction = kGetTypeFuncType.toProto(
context, "${cname}_type",
llvm.module, null,
"${cname}_type", LLVMLinkage.LLVMExternalLinkage
kGetTypeFuncType ).createLlvmFunction(context, llvm.module)
)
val builder = LLVMCreateBuilderInContext(llvm.llvmContext)!! val builder = LLVMCreateBuilderInContext(llvm.llvmContext)!!
val bb = LLVMAppendBasicBlockInContext(llvm.llvmContext, getTypeFunction, "")!! val bb = getTypeFunction.addBasicBlock(llvm.llvmContext)
LLVMPositionBuilderAtEnd(builder, bb) LLVMPositionBuilderAtEnd(builder, bb)
LLVMBuildRet(builder, irClass.typeInfoPtr.llvm) LLVMBuildRet(builder, irClass.typeInfoPtr.llvm)
LLVMDisposeBuilder(builder) LLVMDisposeBuilder(builder)
// Produce instance getter if needed. // Produce instance getter if needed.
if (isSingletonObject) { if (isSingletonObject) {
generateFunction(codegen, kGetObjectFuncType, "${cname}_instance") { val functionProto = kGetObjectFuncType.toProto(
"${cname}_instance",
null,
LLVMLinkage.LLVMExternalLinkage
)
generateFunction(codegen, functionProto) {
val value = call( val value = call(
codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)), codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)),
emptyList(), emptyList(),
@@ -103,7 +102,12 @@ internal class CAdapterCodegen(
isEnumEntry -> { isEnumEntry -> {
// Produce entry getter. // Produce entry getter.
cname = "_konan_function_${owner.nextFunctionIndex()}" cname = "_konan_function_${owner.nextFunctionIndex()}"
generateFunction(codegen, kGetObjectFuncType, cname) { val functionProto = kGetObjectFuncType.toProto(
cname,
null,
LLVMLinkage.LLVMExternalLinkage
)
generateFunction(codegen, functionProto) {
val irEnumEntry = irSymbol.owner as IrEnumEntry val irEnumEntry = irSymbol.owner as IrEnumEntry
val value = getEnumEntry(irEnumEntry, ExceptionHandler.Caller) val value = getEnumEntry(irEnumEntry, ExceptionHandler.Caller)
ret(value) ret(value)
@@ -112,8 +116,8 @@ internal class CAdapterCodegen(
} }
} }
private val kGetTypeFuncType = LLVMFunctionType(codegen.kTypeInfoPtr, null, 0, 0)!! private val kGetTypeFuncType = LlvmFunctionSignature(LlvmRetType(codegen.kTypeInfoPtr))
// Abstraction leak for slot :(. // Abstraction leak for slot :(.
private val kGetObjectFuncType = LLVMFunctionType(codegen.kObjHeaderPtr, cValuesOf(codegen.kObjHeaderPtrPtr), 1, 0)!! private val kGetObjectFuncType = LlvmFunctionSignature(LlvmRetType(codegen.kObjHeaderPtr), listOf(LlvmParamType(codegen.kObjHeaderPtrPtr)))
} }
@@ -24,6 +24,9 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind
internal class CodeGenerator(override val generationState: NativeGenerationState) : ContextUtils { internal class CodeGenerator(override val generationState: NativeGenerationState) : ContextUtils {
fun addFunction(proto: LlvmFunctionProto): LlvmCallable =
proto.createLlvmFunction(context, llvm.module)
fun llvmFunction(function: IrFunction): LlvmCallable = fun llvmFunction(function: IrFunction): LlvmCallable =
llvmFunctionOrNull(function) llvmFunctionOrNull(function)
?: error("no function ${function.name} in ${function.file.fqName}") ?: error("no function ${function.name} in ${function.file.fqName}")
@@ -42,12 +45,7 @@ internal class CodeGenerator(override val generationState: NativeGenerationState
fun typeInfoValue(irClass: IrClass): LLVMValueRef = irClass.llvmTypeInfoPtr fun typeInfoValue(irClass: IrClass): LLVMValueRef = irClass.llvmTypeInfoPtr
fun param(fn: IrFunction, i: Int): LLVMValueRef { fun param(fn: IrFunction, i: Int) = fn.llvmFunction.param(i)
assert(i >= 0 && i < countParams(fn))
return LLVMGetParam(fn.llvmFunction.llvmValue, i)!!
}
private fun countParams(fn: IrFunction) = LLVMCountParams(fn.llvmFunction.llvmValue)
fun functionEntryPointAddress(function: IrFunction) = function.entryPointAddress.llvm fun functionEntryPointAddress(function: IrFunction) = function.entryPointAddress.llvm
@@ -108,7 +106,7 @@ internal inline fun generateFunction(
endLocation: LocationInfo?, endLocation: LocationInfo?,
code: FunctionGenerationContext.() -> Unit code: FunctionGenerationContext.() -> Unit
) { ) {
val llvmFunction = codegen.llvmFunction(function).llvmValue val llvmFunction = codegen.llvmFunction(function)
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
@@ -131,13 +129,9 @@ internal inline fun generateFunction(
} finally { } finally {
functionGenerationContext.dispose() functionGenerationContext.dispose()
} }
// To perform per-function validation.
if (false)
LLVMVerifyFunction(llvmFunction, LLVMVerifierFailureAction.LLVMAbortProcessAction)
} }
internal inline fun <T : FunctionGenerationContext> FunctionGenerationContextBuilder<T>.generate(code: T.() -> Unit): LLVMValueRef { internal inline fun <T : FunctionGenerationContext> FunctionGenerationContextBuilder<T>.generate(code: T.() -> Unit): LlvmCallable {
val functionGenerationContext = this.build() val functionGenerationContext = this.build()
return try { return try {
generateFunctionBody(functionGenerationContext, code) generateFunctionBody(functionGenerationContext, code)
@@ -149,12 +143,13 @@ internal inline fun <T : FunctionGenerationContext> FunctionGenerationContextBui
internal inline fun generateFunction( internal inline fun generateFunction(
codegen: CodeGenerator, codegen: CodeGenerator,
function: LLVMValueRef, functionProto: LlvmFunctionProto,
startLocation: LocationInfo? = null, startLocation: LocationInfo? = null,
endLocation: LocationInfo? = null, endLocation: LocationInfo? = null,
switchToRunnable: Boolean = false, switchToRunnable: Boolean = false,
code: FunctionGenerationContext.() -> Unit code: FunctionGenerationContext.() -> Unit
) { ) : LlvmCallable {
val function = codegen.addFunction(functionProto)
val functionGenerationContext = DefaultFunctionGenerationContext( val functionGenerationContext = DefaultFunctionGenerationContext(
function, function,
codegen, codegen,
@@ -167,31 +162,16 @@ internal inline fun generateFunction(
} finally { } finally {
functionGenerationContext.dispose() functionGenerationContext.dispose()
} }
}
internal inline fun generateFunction(
codegen: CodeGenerator,
functionType: LLVMTypeRef,
name: String,
switchToRunnable: Boolean = false,
block: FunctionGenerationContext.() -> Unit
): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes(
codegen.context,
codegen.llvm.module,
name,
functionType
)
generateFunction(codegen, function, startLocation = null, endLocation = null, switchToRunnable = switchToRunnable, code = block)
return function return function
} }
// TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`. // TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`.
internal inline fun generateFunctionNoRuntime( internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator, codegen: CodeGenerator,
function: LLVMValueRef, functionProto: LlvmFunctionProto,
code: FunctionGenerationContext.() -> Unit, 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)
try { try {
functionGenerationContext.forbidRuntime = true functionGenerationContext.forbidRuntime = true
@@ -203,21 +183,6 @@ internal inline fun generateFunctionNoRuntime(
} finally { } finally {
functionGenerationContext.dispose() functionGenerationContext.dispose()
} }
}
internal inline fun generateFunctionNoRuntime(
codegen: CodeGenerator,
functionType: LLVMTypeRef,
name: String,
code: FunctionGenerationContext.() -> Unit,
): LLVMValueRef {
val function = addLlvmFunctionWithDefaultAttributes(
codegen.context,
codegen.llvm.module,
name,
functionType
)
generateFunctionNoRuntime(codegen, function, code)
return function return function
} }
@@ -413,17 +378,12 @@ internal class StackLocalsManagerImpl(
} }
internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationContext>( internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationContext>(
val function: LLVMValueRef, val function: LlvmCallable,
val codegen: CodeGenerator val codegen: CodeGenerator
) { ) {
constructor(functionType: LLVMTypeRef, functionName: String, codegen: CodeGenerator) : constructor(functionProto: LlvmFunctionProto, codegen: CodeGenerator) :
this( this(
addLlvmFunctionWithDefaultAttributes( codegen.addFunction(functionProto),
codegen.context,
codegen.llvm.module,
functionName,
functionType
),
codegen codegen
) )
@@ -436,7 +396,7 @@ internal abstract class FunctionGenerationContextBuilder<T : FunctionGenerationC
} }
internal abstract class FunctionGenerationContext( internal abstract class FunctionGenerationContext(
val function: LLVMValueRef, val function: LlvmCallable,
val codegen: CodeGenerator, val codegen: CodeGenerator,
private val startLocation: LocationInfo?, private val startLocation: LocationInfo?,
protected val endLocation: LocationInfo?, protected val endLocation: LocationInfo?,
@@ -463,7 +423,7 @@ internal abstract class FunctionGenerationContext(
basicBlockToLastLocation.put(block, LocationInfoRange(startLocationInfo, endLocation)) basicBlockToLastLocation.put(block, LocationInfoRange(startLocationInfo, endLocation))
} }
var returnType: LLVMTypeRef? = LLVMGetReturnType(getFunctionType(function)) var returnType: LLVMTypeRef? = function.returnType
val constructedClass: IrClass? val constructedClass: IrClass?
get() = (irFunction as? IrConstructor)?.constructedClass get() = (irFunction as? IrConstructor)?.constructedClass
var returnSlot: LLVMValueRef? = null var returnSlot: LLVMValueRef? = null
@@ -499,11 +459,9 @@ internal abstract class FunctionGenerationContext(
data class FunctionInvokeInformation( data class FunctionInvokeInformation(
val invokeInstruction: LLVMValueRef, val invokeInstruction: LLVMValueRef,
val llvmFunction: LLVMValueRef, val llvmFunction: LlvmCallable,
val rargs: CValues<CPointerVar<LLVMOpaqueValue>>, val args: List<LLVMValueRef>,
val argsNumber: Int,
val success: LLVMBasicBlockRef, val success: LLVMBasicBlockRef,
val attributeProvider: LlvmFunctionAttributeProvider?
) )
private val invokeInstructions = mutableListOf<FunctionInvokeInformation>() private val invokeInstructions = mutableListOf<FunctionInvokeInformation>()
@@ -519,21 +477,12 @@ internal abstract class FunctionGenerationContext(
// TODO: Consider using a different abstraction than `FunctionGenerationContext`. // TODO: Consider using a different abstraction than `FunctionGenerationContext`.
var forbidRuntime = false var forbidRuntime = false
init {
irFunction?.let {
if (!irFunction.isExported()) {
if (!context.config.producePerFileCache || irFunction !in generationState.calledFromExportedInlineFunctions)
LLVMSetLinkage(function, LLVMLinkage.LLVMInternalLinkage) // (Cannot do this before the function body is created)
}
}
}
fun dispose() { fun dispose() {
currentPositionHolder.dispose() currentPositionHolder.dispose()
} }
protected fun basicBlockInFunction(name: String, locationInfo: LocationInfo?): LLVMBasicBlockRef { protected fun basicBlockInFunction(name: String, locationInfo: LocationInfo?): LLVMBasicBlockRef {
val bb = LLVMAppendBasicBlockInContext(llvm.llvmContext, function, name)!! val bb = function.addBasicBlock(llvm.llvmContext, name)
update(bb, locationInfo) update(bb, locationInfo)
return bb return bb
} }
@@ -585,7 +534,7 @@ internal abstract class FunctionGenerationContext(
abstract fun ret(value: LLVMValueRef?): LLVMValueRef abstract fun ret(value: LLVMValueRef?): LLVMValueRef
fun param(index: Int): LLVMValueRef = LLVMGetParam(this.function, index)!! fun param(index: Int): LLVMValueRef = function.param(index)
fun load(address: LLVMValueRef, name: String = "", fun load(address: LLVMValueRef, name: String = "",
memoryOrder: LLVMAtomicOrdering? = null, alignment: Int? = null memoryOrder: LLVMAtomicOrdering? = null, alignment: Int? = null
@@ -699,17 +648,8 @@ internal abstract class FunctionGenerationContext(
exceptionHandler: ExceptionHandler = ExceptionHandler.None, exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false, verbatim: Boolean = false,
resultSlot: LLVMValueRef? = null, resultSlot: LLVMValueRef? = null,
): LLVMValueRef =
call(llvmCallable.llvmValue, args, resultLifetime, exceptionHandler, verbatim, llvmCallable.attributeProvider, resultSlot)
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false,
attributeProvider: LlvmFunctionAttributeProvider? = null,
resultSlot: LLVMValueRef? = null
): LLVMValueRef { ): LLVMValueRef {
val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) { val callArgs = if (verbatim || !isObjectType(llvmCallable.returnType)) {
args args
} else { } else {
// If function returns an object - create slot for the returned value or give local arena. // If function returns an object - create slot for the returned value or give local arena.
@@ -719,7 +659,7 @@ internal abstract class FunctionGenerationContext(
SlotType.STACK -> { SlotType.STACK -> {
localAllocs++ localAllocs++
// Case of local call. Use memory allocated on stack. // Case of local call. Use memory allocated on stack.
val type = LLVMGetReturnType(LLVMGetElementType(llvmFunction.type))!! val type = llvmCallable.returnType
val stackPointer = alloca(type) val stackPointer = alloca(type)
//val objectHeader = structGep(stackPointer, 0) //val objectHeader = structGep(stackPointer, 0)
//setTypeInfoForLocalObject(objectHeader) //setTypeInfoForLocalObject(objectHeader)
@@ -735,18 +675,13 @@ internal abstract class FunctionGenerationContext(
} }
args + realResultSlot args + realResultSlot
} }
return callRaw(llvmFunction, callArgs, exceptionHandler, attributeProvider) return callRaw(llvmCallable, callArgs, exceptionHandler)
} }
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, private fun callRaw(llvmCallable: LlvmCallable, args: List<LLVMValueRef>,
exceptionHandler: ExceptionHandler, exceptionHandler: ExceptionHandler): LLVMValueRef {
attributeProvider: LlvmFunctionAttributeProvider?): LLVMValueRef { if (llvmCallable.isNoUnwind) {
val rargs = args.toCValues() return llvmCallable.buildCall(builder, args)
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
isFunctionNoUnwind(llvmFunction)) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!.also {
attributeProvider?.addCallSiteAttributes(it)
}
} else { } else {
val unwind = when (exceptionHandler) { val unwind = when (exceptionHandler) {
ExceptionHandler.Caller -> cleanupLandingpad ExceptionHandler.Caller -> cleanupLandingpad
@@ -756,7 +691,7 @@ internal abstract class FunctionGenerationContext(
// When calling a function that is not marked as nounwind (can throw an exception), // When calling a function that is not marked as nounwind (can throw an exception),
// it is required to specify an unwind label to handle exceptions properly. // it is required to specify an unwind label to handle exceptions properly.
// Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`. // Runtime C++ function can be marked as non-throwing using `RUNTIME_NOTHROW`.
val functionName = llvmFunction.name val functionName = llvmCallable.name
val message = val message =
"no exception handler specified when calling function $functionName without nounwind attr" "no exception handler specified when calling function $functionName without nounwind attr"
throw IllegalArgumentException(message) throw IllegalArgumentException(message)
@@ -766,13 +701,12 @@ internal abstract class FunctionGenerationContext(
val position = position() val position = position()
val endLocation = position?.end val endLocation = position?.end
val success = basicBlock("call_success", endLocation) val success = basicBlock("call_success", endLocation)
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, unwind, "")!! val result = llvmCallable.buildInvoke(builder, args, success, unwind)
attributeProvider?.addCallSiteAttributes(result)
// Store invoke instruction and its success block in reverse order. // Store invoke instruction and its success block in reverse order.
// Reverse order allows save arguments valid during all work with invokes // Reverse order allows save arguments valid during all work with invokes
// because other invokes processed before can be inside arguments list. // because other invokes processed before can be inside arguments list.
if (exceptionHandler == ExceptionHandler.Caller) if (exceptionHandler == ExceptionHandler.Caller)
invokeInstructions.add(0, FunctionInvokeInformation(result, llvmFunction, rargs, args.size, success, attributeProvider)) invokeInstructions.add(0, FunctionInvokeInformation(result, llvmCallable, args, success))
positionAtEnd(success) positionAtEnd(success)
return result return result
@@ -846,7 +780,7 @@ internal abstract class FunctionGenerationContext(
} }
fun blockAddress(bbLabel: LLVMBasicBlockRef): LLVMValueRef { fun blockAddress(bbLabel: LLVMBasicBlockRef): LLVMValueRef {
return LLVMBlockAddress(function, bbLabel)!! return function.blockAddress(bbLabel)
} }
fun not(arg: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildNot(builder, arg, name)!! fun not(arg: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildNot(builder, arg, name)!!
@@ -927,12 +861,11 @@ internal abstract class FunctionGenerationContext(
LLVMBuildExtractValue(builder, aggregate, index, name)!! LLVMBuildExtractValue(builder, aggregate, index, name)!!
fun gxxLandingpad(numClauses: Int, name: String = "", switchThreadState: Boolean = false): LLVMValueRef { fun gxxLandingpad(numClauses: Int, name: String = "", switchThreadState: Boolean = false): LLVMValueRef {
val personalityFunction = llvm.gxxPersonalityFunction.llvmValue val personalityFunction = llvm.gxxPersonalityFunction
// Type of `landingpad` instruction result (depends on personality function): // Type of `landingpad` instruction result (depends on personality function):
val landingpadType = llvm.structType(llvm.int8PtrType, llvm.int32Type) val landingpadType = llvm.structType(llvm.int8PtrType, llvm.int32Type)
val landingpad = personalityFunction.buildLandingpad(builder, landingpadType, numClauses, name)
val landingpad = LLVMBuildLandingPad(builder, landingpadType, personalityFunction, numClauses, name)!!
if (switchThreadState) { if (switchThreadState) {
switchThreadState(Runnable) switchThreadState(Runnable)
@@ -1286,7 +1219,7 @@ internal abstract class FunctionGenerationContext(
val enumClass = enumEntry.parentAsClass val enumClass = enumEntry.parentAsClass
val getterId = context.enumsSupport.enumEntriesMap(enumClass)[enumEntry.name]!!.getterId val getterId = context.enumsSupport.enumEntriesMap(enumClass)[enumEntry.name]!!.getterId
return call( return call(
context.enumsSupport.getValueGetter(enumClass).llvmFunction.llvmValue, context.enumsSupport.getValueGetter(enumClass).llvmFunction,
listOf(llvm.int32(getterId)), listOf(llvm.int32(getterId)),
Lifetime.GLOBAL, Lifetime.GLOBAL,
exceptionHandler exceptionHandler
@@ -1356,7 +1289,7 @@ internal abstract class FunctionGenerationContext(
internal fun prologue() { internal fun prologue() {
if (isObjectType(returnType!!)) { if (isObjectType(returnType!!)) {
returnSlot = LLVMGetParam(function, numParameters(function.type) - 1) returnSlot = function.param( function.numParams - 1)
} }
positionAtEnd(localsInitBb) positionAtEnd(localsInitBb)
@@ -1449,9 +1382,7 @@ internal abstract class FunctionGenerationContext(
// Replace invokes with calls and branches. // Replace invokes with calls and branches.
invokeInstructions.forEach { functionInvokeInfo -> invokeInstructions.forEach { functionInvokeInfo ->
positionBefore(functionInvokeInfo.invokeInstruction) positionBefore(functionInvokeInfo.invokeInstruction)
val newResult = LLVMBuildCall(builder, functionInvokeInfo.llvmFunction, functionInvokeInfo.rargs, val newResult = functionInvokeInfo.llvmFunction.buildCall(builder, functionInvokeInfo.args)
functionInvokeInfo.argsNumber, "")!!
functionInvokeInfo.attributeProvider?.addCallSiteAttributes(newResult)
// Have to generate `br` instruction because of current scheme of debug info. // Have to generate `br` instruction because of current scheme of debug info.
br(functionInvokeInfo.success) br(functionInvokeInfo.success)
LLVMReplaceAllUsesWith(functionInvokeInfo.invokeInstruction, newResult) LLVMReplaceAllUsesWith(functionInvokeInfo.invokeInstruction, newResult)
@@ -1636,7 +1567,7 @@ internal abstract class FunctionGenerationContext(
} }
internal class DefaultFunctionGenerationContext( internal class DefaultFunctionGenerationContext(
function: LLVMValueRef, function: LlvmCallable,
codegen: CodeGenerator, codegen: CodeGenerator,
startLocation: LocationInfo?, startLocation: LocationInfo?,
endLocation: LocationInfo?, endLocation: LocationInfo?,
@@ -184,7 +184,7 @@ internal interface ContextUtils : RuntimeAware {
?: context.irLinker.getExternalDeclarationFileName(this) ?: context.irLinker.getExternalDeclarationFileName(this)
this.computePrivateSymbolName(containerName) this.computePrivateSymbolName(containerName)
} }
val proto = LlvmFunctionProto(this, symbolName, this@ContextUtils) val proto = LlvmFunctionProto(this, symbolName, this@ContextUtils, LLVMLinkage.LLVMExternalLinkage)
llvm.externalFunction(proto) llvm.externalFunction(proto)
} }
} else { } else {
@@ -197,8 +197,7 @@ internal interface ContextUtils : RuntimeAware {
*/ */
val IrFunction.entryPointAddress: ConstPointer val IrFunction.entryPointAddress: ConstPointer
get() { get() {
val result = LLVMConstBitCast(this.llvmFunction.llvmValue, llvm.int8PtrType)!! return llvmFunction.toConstPointer().bitcast(llvm.int8PtrType)
return constPointer(result)
} }
val IrClass.typeInfoPtr: ConstPointer val IrClass.typeInfoPtr: ConstPointer
@@ -356,19 +355,19 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
} }
internal fun externalFunction(llvmFunctionProto: LlvmFunctionProto): LlvmCallable { internal fun externalFunction(llvmFunctionProto: LlvmFunctionProto): LlvmCallable {
this.dependenciesTracker.add(llvmFunctionProto.origin, onlyBitcode = llvmFunctionProto.independent) if (llvmFunctionProto.origin != null) {
this.dependenciesTracker.add(llvmFunctionProto.origin, onlyBitcode = llvmFunctionProto.independent)
}
val found = LLVMGetNamedFunction(module, llvmFunctionProto.name) val found = LLVMGetNamedFunction(module, llvmFunctionProto.name)
if (found != null) { if (found != null) {
assert(getFunctionType(found) == llvmFunctionProto.llvmFunctionType) { require(getFunctionType(found) == llvmFunctionProto.signature.llvmFunctionType) {
"Expected: ${LLVMPrintTypeToString(llvmFunctionProto.llvmFunctionType)!!.toKString()} " + "Expected: ${LLVMPrintTypeToString(llvmFunctionProto.signature.llvmFunctionType)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}" "found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
} }
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage) require(LLVMGetLinkage(found) == llvmFunctionProto.linkage)
return LlvmCallable(found, llvmFunctionProto) return LlvmCallable(found, llvmFunctionProto.signature)
} else { } else {
val function = addLlvmFunctionWithDefaultAttributes(context, module, llvmFunctionProto.name, llvmFunctionProto.llvmFunctionType) return llvmFunctionProto.createLlvmFunction(context, module)
llvmFunctionProto.addFunctionAttributes(function)
return LlvmCallable(function, llvmFunctionProto)
} }
} }
@@ -379,9 +378,12 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
functionAttributes: List<LlvmFunctionAttribute> = emptyList(), functionAttributes: List<LlvmFunctionAttribute> = emptyList(),
isVararg: Boolean = false isVararg: Boolean = false
) = externalFunction( ) = externalFunction(
LlvmFunctionProto(name, returnType, parameterTypes, functionAttributes, LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes).toProto(
name,
origin = FunctionOrigin.FromNativeRuntime, origin = FunctionOrigin.FromNativeRuntime,
isVararg, independent = false) linkage = LLVMLinkage.LLVMExternalLinkage,
independent = false
)
) )
internal fun externalNativeRuntimeFunction(name: String, signature: LlvmFunctionSignature) = internal fun externalNativeRuntimeFunction(name: String, signature: LlvmFunctionSignature) =
@@ -479,7 +481,6 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
val CompareAndSwapVolatileHeapRef by lazyRtFunction val CompareAndSwapVolatileHeapRef by lazyRtFunction
val GetAndSetVolatileHeapRef by lazyRtFunction val GetAndSetVolatileHeapRef by lazyRtFunction
val tlsMode by lazy { val tlsMode by lazy {
when (target) { when (target) {
KonanTarget.WASM32, KonanTarget.WASM32,
@@ -488,12 +489,11 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
} }
} }
val usedFunctions = mutableListOf<LLVMValueRef>() val usedFunctions = mutableListOf<LlvmCallable>()
val usedGlobals = mutableListOf<LLVMValueRef>() val usedGlobals = mutableListOf<LLVMValueRef>()
val compilerUsedGlobals = mutableListOf<LLVMValueRef>() val compilerUsedGlobals = mutableListOf<LLVMValueRef>()
val irStaticInitializers = mutableListOf<IrStaticInitializer>() val irStaticInitializers = mutableListOf<IrStaticInitializer>()
val otherStaticInitializers = mutableListOf<LLVMValueRef>() val otherStaticInitializers = mutableListOf<LlvmCallable>()
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
val initializersGenerationState = InitializersGenerationState() val initializersGenerationState = InitializersGenerationState()
val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>() val boxCacheGlobals = mutableMapOf<BoxCache, StaticData.Global>()
@@ -605,25 +605,23 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind) functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind)
) )
private fun getSizeOfReturnTypeInBits(functionPointer: LLVMValueRef): Long { private fun getSizeOfTypeInBits(type: LLVMTypeRef): Long {
// LLVMGetElementType is called because we need to dereference a pointer to function. return LLVMSizeOfTypeInBits(runtime.targetData, type)
val nsIntegerType = LLVMGetReturnType(LLVMGetElementType(functionPointer.type))
return LLVMSizeOfTypeInBits(runtime.targetData, nsIntegerType)
} }
/** /**
* Width of NSInteger in bits. * Width of NSInteger in bits.
*/ */
val nsIntegerTypeWidth: Long by lazy { val nsIntegerTypeWidth: Long by lazy {
getSizeOfReturnTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider.llvmValue) getSizeOfTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider.returnType)
} }
/** /**
* Width of C long type in bits. * Width of C long type in bits.
*/ */
val longTypeWidth: Long by lazy { val longTypeWidth: Long by lazy {
getSizeOfReturnTypeInBits(Kotlin_longTypeProvider.llvmValue) getSizeOfTypeInBits(Kotlin_longTypeProvider.returnType)
} }
} }
class IrStaticInitializer(val konanLibrary: KotlinLibrary?, val initializer: LLVMValueRef) class IrStaticInitializer(val konanLibrary: KotlinLibrary?, val initializer: LlvmCallable)
@@ -132,7 +132,7 @@ internal class DebugInfo(override val generationState: NativeGenerationState) :
} }
val files = mutableMapOf<String, DIFileRef>() val files = mutableMapOf<String, DIFileRef>()
val subprograms = mutableMapOf<LLVMValueRef, DISubprogramRef>() val subprograms = mutableMapOf<LlvmCallable, DISubprogramRef>()
/* Some functions are inlined on all callsites and body is eliminated by DCE, so there's no LLVM value */ /* Some functions are inlined on all callsites and body is eliminated by DCE, so there's no LLVM value */
val inlinedSubprograms = mutableMapOf<IrFunction, DISubprogramRef>() val inlinedSubprograms = mutableMapOf<IrFunction, DISubprogramRef>()
@@ -264,7 +264,7 @@ internal fun String?.toFileAndFolder(config: KonanConfig): FileAndFolder {
internal fun alignTo(value: Long, align: Long): Long = (value + align - 1) / align * align internal fun alignTo(value: Long, align: Long): Long = (value + align - 1) / align * align
internal fun setupBridgeDebugInfo(generationState: NativeGenerationState, function: LLVMValueRef): LocationInfo? { internal fun setupBridgeDebugInfo(generationState: NativeGenerationState, function: LlvmCallable): LocationInfo? {
if (!generationState.shouldContainLocationDebugInfo()) { if (!generationState.shouldContainLocationDebugInfo()) {
return null return null
} }
@@ -273,17 +273,16 @@ internal fun setupBridgeDebugInfo(generationState: NativeGenerationState, functi
val file = debugInfo.compilerGeneratedFile val file = debugInfo.compilerGeneratedFile
// TODO: can we share the scope among all bridges? // TODO: can we share the scope among all bridges?
val scope: DIScopeOpaqueRef = DICreateBridgeFunction( val scope: DIScopeOpaqueRef = function.createBridgeFunctionDebugInfo(
builder = debugInfo.builder, builder = debugInfo.builder,
scope = file.reinterpret(), scope = file.reinterpret(),
function = function,
file = file, file = file,
lineNo = 0, lineNo = 0,
type = debugInfo.subroutineType(generationState.runtime.targetData, emptyList()), // TODO: use proper type. type = debugInfo.subroutineType(generationState.runtime.targetData, emptyList()), // TODO: use proper type.
isLocal = 0, isLocal = 0,
isDefinition = 1, isDefinition = 1,
scopeLine = 0 scopeLine = 0
)!!.reinterpret() ).reinterpret()
return LocationInfo(scope, 1, 0) return LocationInfo(scope, 1, 0)
} }
@@ -559,8 +559,8 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
val superClass = args.single() val superClass = args.single()
val messenger = LLVMBuildSelect(builder, val messenger = LLVMBuildSelect(builder,
If = icmpEq(superClass, llvm.kNullInt8Ptr), If = icmpEq(superClass, llvm.kNullInt8Ptr),
Then = normalMessenger.llvmValue, Then = normalMessenger.toConstPointer().llvm,
Else = superMessenger.llvmValue, Else = superMessenger.toConstPointer().llvm,
Name = "" Name = ""
)!! )!!
@@ -84,6 +84,14 @@ internal fun IrField.isGlobalNonPrimitive(context: Context) = when {
internal fun IrField.shouldBeFrozen(context: Context): Boolean = internal fun IrField.shouldBeFrozen(context: Context): Boolean =
this.storageKind(context) == FieldStorageKind.SHARED_FROZEN this.storageKind(context) == FieldStorageKind.SHARED_FROZEN
internal fun IrFunction.shouldGenerateBody(): Boolean = when {
this is IrConstructor && constructedClass.isInlined() -> false
this is IrConstructor && isObjCConstructor -> false
this is IrSimpleFunction && modality == Modality.ABSTRACT -> false
isExternal -> false
else -> true
}
internal class RTTIGeneratorVisitor(generationState: NativeGenerationState, referencedFunctions: Set<IrFunction>?) : IrElementVisitorVoid { internal class RTTIGeneratorVisitor(generationState: NativeGenerationState, referencedFunctions: Set<IrFunction>?) : IrElementVisitorVoid {
val generator = RTTIGenerator(generationState, referencedFunctions) val generator = RTTIGenerator(generationState, referencedFunctions)
@@ -441,7 +449,7 @@ internal class CodeGeneratorVisitor(
generationState.coverage.writeRegionInfo() generationState.coverage.writeRegionInfo()
overrideRuntimeGlobals() overrideRuntimeGlobals()
appendLlvmUsed("llvm.used", llvm.usedFunctions + llvm.usedGlobals) appendLlvmUsed("llvm.used", llvm.usedFunctions.map { it.toConstPointer().llvm } + llvm.usedGlobals)
appendLlvmUsed("llvm.compiler.used", llvm.compilerUsedGlobals) appendLlvmUsed("llvm.compiler.used", llvm.compilerUsedGlobals)
if (context.config.produce.isNativeLibrary) { if (context.config.produce.isNativeLibrary) {
context.cAdapterExportedElements?.let { appendCAdapters(it) } context.cAdapterExportedElements?.let { appendCAdapters(it) }
@@ -453,10 +461,10 @@ internal class CodeGeneratorVisitor(
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
val kVoidFuncType = functionType(llvm.voidType) val ctorFunctionSignature = LlvmFunctionSignature(LlvmRetType(llvm.voidType))
val kNodeInitType = LLVMGetTypeByName(llvm.module, "struct.InitNode")!! val kNodeInitType = LLVMGetTypeByName(llvm.module, "struct.InitNode")!!
val kMemoryStateType = LLVMGetTypeByName(llvm.module, "struct.MemoryState")!! val kMemoryStateType = LLVMGetTypeByName(llvm.module, "struct.MemoryState")!!
val kInitFuncType = functionType(llvm.voidType, false, llvm.int32Type, pointerType(kMemoryStateType)) val kInitFuncType = LlvmFunctionSignature(LlvmRetType(llvm.voidType), listOf(LlvmParamType(llvm.int32Type), LlvmParamType(pointerType(kMemoryStateType))))
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -469,16 +477,10 @@ internal class CodeGeneratorVisitor(
val FILE_NOT_INITIALIZED = 0 val FILE_NOT_INITIALIZED = 0
val FILE_INITIALIZED = 2 val FILE_INITIALIZED = 2
private fun createInitBody(state: ScopeInitializersGenerationState): LLVMValueRef { private fun createInitBody(state: ScopeInitializersGenerationState): LlvmCallable {
val initFunction = addLlvmFunctionWithDefaultAttributes( val initFunctionProto = kInitFuncType.toProto("", null, LLVMLinkage.LLVMPrivateLinkage)
generationState.context, return generateFunction(codegen, initFunctionProto) {
llvm.module, using(FunctionScope(function, this)) {
"",
kInitFuncType
)
LLVMSetLinkage(initFunction, LLVMLinkage.LLVMPrivateLinkage)
generateFunction(codegen, initFunction) {
using(FunctionScope(initFunction, this)) {
val bbInit = basicBlock("init", null) val bbInit = basicBlock("init", null)
val bbLocalInit = basicBlock("local_init", null) val bbLocalInit = basicBlock("local_init", null)
val bbLocalAlloc = basicBlock("local_alloc", null) val bbLocalAlloc = basicBlock("local_alloc", null)
@@ -487,7 +489,7 @@ internal class CodeGeneratorVisitor(
unreachable() unreachable()
} }
switch(LLVMGetParam(initFunction, 0)!!, switch(function.param(0),
listOf(llvm.int32(INIT_GLOBALS) to bbInit, listOf(llvm.int32(INIT_GLOBALS) to bbInit,
llvm.int32(INIT_THREAD_LOCAL_GLOBALS) to bbLocalInit, llvm.int32(INIT_THREAD_LOCAL_GLOBALS) to bbLocalInit,
llvm.int32(ALLOC_THREAD_LOCAL_GLOBALS) to bbLocalAlloc, llvm.int32(ALLOC_THREAD_LOCAL_GLOBALS) to bbLocalAlloc,
@@ -513,7 +515,7 @@ internal class CodeGeneratorVisitor(
appendingTo(bbLocalAlloc) { appendingTo(bbLocalAlloc) {
if (llvm.tlsCount > 0) { if (llvm.tlsCount > 0) {
val memory = LLVMGetParam(initFunction, 1)!! val memory = function.param(1)
call(llvm.addTLSRecord, listOf(memory, llvm.tlsKey, llvm.int32(llvm.tlsCount))) call(llvm.addTLSRecord, listOf(memory, llvm.tlsKey, llvm.int32(llvm.tlsCount)))
} }
ret(null) ret(null)
@@ -538,15 +540,14 @@ internal class CodeGeneratorVisitor(
} }
} }
} }
return initFunction
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
// Creates static struct InitNode $nodeName = {$initName, NULL}; // Creates static struct InitNode $nodeName = {$initName, NULL};
private fun createInitNode(initFunction: LLVMValueRef): LLVMValueRef { private fun createInitNode(initFunction: LlvmCallable): LLVMValueRef {
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) val nextInitNode = LLVMConstNull(pointerType(kNodeInitType))
val argList = cValuesOf(initFunction, nextInitNode) val argList = cValuesOf(initFunction.toConstPointer().llvm, nextInitNode)
// Create static object of class InitNode. // Create static object of class InitNode.
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!!
// Create global variable with init record data. // Create global variable with init record data.
@@ -555,13 +556,13 @@ internal class CodeGeneratorVisitor(
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun createInitCtor(initNodePtr: LLVMValueRef): LLVMValueRef { private fun createInitCtor(initNodePtr: LLVMValueRef): LlvmCallable {
val ctorFunction = generateFunctionNoRuntime(codegen, kVoidFuncType, "") { val ctorProto = ctorFunctionSignature.toProto("", null, LLVMLinkage.LLVMPrivateLinkage)
val ctor = generateFunctionNoRuntime(codegen, ctorProto) {
call(llvm.appendToInitalizersTail, listOf(initNodePtr)) call(llvm.appendToInitalizersTail, listOf(initNodePtr))
ret(null) ret(null)
} }
LLVMSetLinkage(ctorFunction, LLVMLinkage.LLVMPrivateLinkage) return ctor
return ctorFunction
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -623,16 +624,6 @@ internal class CodeGeneratorVisitor(
override fun visitConstructor(declaration: IrConstructor) { override fun visitConstructor(declaration: IrConstructor) {
context.log{"visitConstructor : ${ir2string(declaration)}"} context.log{"visitConstructor : ${ir2string(declaration)}"}
if (declaration.constructedClass.isInlined()) {
// Do not generate any ctors for value types.
return
}
if (declaration.isObjCConstructor) {
// Do not generate any ctors for external Objective-C classes.
return
}
visitFunction(declaration) visitFunction(declaration)
} }
@@ -709,12 +700,12 @@ internal class CodeGeneratorVisitor(
private inner class FunctionScope private constructor( private inner class FunctionScope private constructor(
val functionGenerationContext: FunctionGenerationContext, val functionGenerationContext: FunctionGenerationContext,
val declaration: IrFunction?, val declaration: IrFunction?,
val llvmFunction: LLVMValueRef) : InnerScopeImpl() { val llvmFunction: LlvmCallable) : InnerScopeImpl() {
constructor(declaration: IrFunction, functionGenerationContext: FunctionGenerationContext) : constructor(declaration: IrFunction, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, declaration, codegen.llvmFunction(declaration).llvmValue) this(functionGenerationContext, declaration, codegen.llvmFunction(declaration))
constructor(llvmFunction: LLVMValueRef, functionGenerationContext: FunctionGenerationContext) : constructor(llvmFunction: LlvmCallable, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, null, llvmFunction) this(functionGenerationContext, null, llvmFunction)
val coverageInstrumentation: LLVMCoverageInstrumentation? = val coverageInstrumentation: LLVMCoverageInstrumentation? =
@@ -796,12 +787,10 @@ internal class CodeGeneratorVisitor(
override fun visitFunction(declaration: IrFunction) { override fun visitFunction(declaration: IrFunction) {
context.log{"visitFunction : ${ir2string(declaration)}"} context.log{"visitFunction : ${ir2string(declaration)}"}
val body = declaration.body
val scopeState = llvm.initializersGenerationState.scopeState val scopeState = llvm.initializersGenerationState.scopeState
if (declaration.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER) { if (declaration.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER) {
require(scopeState.globalInitFunction == null) { "There can only be at most one global file initializer" } require(scopeState.globalInitFunction == null) { "There can only be at most one global file initializer" }
require(body == null) { "The body of file initializer should be null" } require(declaration.body == null) { "The body of file initializer should be null" }
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" } require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
require(declaration.returnsUnit()) { "File initializer must return Unit" } require(declaration.returnsUnit()) { "File initializer must return Unit" }
scopeState.globalInitFunction = declaration scopeState.globalInitFunction = declaration
@@ -810,7 +799,7 @@ internal class CodeGeneratorVisitor(
if (declaration.origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER if (declaration.origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|| declaration.origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER) { || declaration.origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER) {
require(scopeState.threadLocalInitFunction == null) { "There can only be at most one thread local file initializer" } require(scopeState.threadLocalInitFunction == null) { "There can only be at most one thread local file initializer" }
require(body == null) { "The body of file initializer should be null" } require(declaration.body == null) { "The body of file initializer should be null" }
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" } require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
require(declaration.returnsUnit()) { "File initializer must return Unit" } require(declaration.returnsUnit()) { "File initializer must return Unit" }
scopeState.threadLocalInitFunction = declaration scopeState.threadLocalInitFunction = declaration
@@ -818,10 +807,10 @@ internal class CodeGeneratorVisitor(
} }
if ((declaration as? IrSimpleFunction)?.modality == Modality.ABSTRACT if (!declaration.shouldGenerateBody())
|| declaration.isExternal
|| body == null)
return return
// Some special functions may have empty body, thay are handled separetely.
val body = declaration.body ?: return
val file = if (declaration.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) val file = if (declaration.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA)
null null
else ((declaration as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.let { context.irLinker.getFileOf(it) }?.takeIf { else ((declaration as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.let { context.irLinker.getFileOf(it) }?.takeIf {
@@ -860,7 +849,7 @@ internal class CodeGeneratorVisitor(
if (declaration.retainAnnotation(context.config.target)) { if (declaration.retainAnnotation(context.config.target)) {
llvm.usedFunctions.add(codegen.llvmFunction(declaration).llvmValue) llvm.usedFunctions.add(codegen.llvmFunction(declaration))
} }
if (context.shouldVerifyBitCode()) if (context.shouldVerifyBitCode())
@@ -2237,8 +2226,8 @@ internal class CodeGeneratorVisitor(
isReifiedInline -> null isReifiedInline -> null
// TODO: May be tie up inline lambdas to their outer function? // TODO: May be tie up inline lambdas to their outer function?
codegen.isExternal(this) && !KonanBinaryInterface.isExported(this) -> null codegen.isExternal(this) && !KonanBinaryInterface.isExported(this) -> null
this is IrSimpleFunction && isSuspend -> this.getOrCreateFunctionWithContinuationStub(context).let { codegen.llvmFunctionOrNull(it)?.llvmValue } this is IrSimpleFunction && isSuspend -> this.getOrCreateFunctionWithContinuationStub(context).let { codegen.llvmFunctionOrNull(it) }
else -> codegen.llvmFunctionOrNull(this)?.llvmValue else -> codegen.llvmFunctionOrNull(this)
} }
return with(debugInfo) { return with(debugInfo) {
val f = this@scope val f = this@scope
@@ -2249,7 +2238,7 @@ internal class CodeGeneratorVisitor(
val subroutineType = subroutineType(codegen.llvmTargetData) val subroutineType = subroutineType(codegen.llvmTargetData)
diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType, nodebug).also { diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType, nodebug).also {
if (!this@scope.isInline) if (!this@scope.isInline)
DIFunctionAddSubprogram(functionLlvmValue, it) functionLlvmValue.addDebugInfoSubprogram(it)
} }
} }
} as DIScopeOpaqueRef } as DIScopeOpaqueRef
@@ -2266,10 +2255,10 @@ internal class CodeGeneratorVisitor(
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef, nodebug: Boolean): DIScopeOpaqueRef? { private fun LlvmCallable.scope(startLine:Int, subroutineType: DISubroutineTypeRef, nodebug: Boolean): DIScopeOpaqueRef? {
return debugInfo.subprograms.getOrPut(this) { return debugInfo.subprograms.getOrPut(this) {
diFunctionScope(name!!, name!!, startLine, subroutineType, nodebug).also { diFunctionScope(name!!, name!!, startLine, subroutineType, nodebug).also {
DIFunctionAddSubprogram(this@scope, it) this@scope.addDebugInfoSubprogram(it)
} }
} as DIScopeOpaqueRef } as DIScopeOpaqueRef
} }
@@ -2425,7 +2414,7 @@ internal class CodeGeneratorVisitor(
private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) { private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer) val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.asCallback() }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbExit = basicBlock("label_continue", null) val bbExit = basicBlock("label_continue", null)
@@ -2445,7 +2434,7 @@ internal class CodeGeneratorVisitor(
val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer) val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer) val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
val localStatePtr = localState.getAddress(functionGenerationContext) val localStatePtr = localState.getAddress(functionGenerationContext)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.asCallback() }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbCheckLocalState = basicBlock("label_check_local", null) val bbCheckLocalState = basicBlock("label_check_local", null)
@@ -2471,7 +2460,7 @@ internal class CodeGeneratorVisitor(
private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) { private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
val state = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer) val state = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
val statePtr = state.getAddress(functionGenerationContext) val statePtr = state.getAddress(functionGenerationContext)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.asCallback() }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbExit = basicBlock("label_continue", null) val bbExit = basicBlock("label_continue", null)
@@ -2544,8 +2533,9 @@ internal class CodeGeneratorVisitor(
val protocolGetterName = annotation.getAnnotationStringValue("protocolGetter") val protocolGetterName = annotation.getAnnotationStringValue("protocolGetter")
val protocolGetterProto = LlvmFunctionProto( val protocolGetterProto = LlvmFunctionProto(
protocolGetterName, protocolGetterName,
LlvmRetType(llvm.int8PtrType), LlvmFunctionSignature(LlvmRetType(llvm.int8PtrType)),
origin = FunctionOrigin.OwnedBy(irClass), origin = FunctionOrigin.OwnedBy(irClass),
linkage = LLVMLinkage.LLVMExternalLinkage,
independent = true // Protocol is header-only declaration. independent = true // Protocol is header-only declaration.
) )
val protocolGetter = llvm.externalFunction(protocolGetterProto) val protocolGetter = llvm.externalFunction(protocolGetterProto)
@@ -2758,12 +2748,12 @@ internal class CodeGeneratorVisitor(
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
// Create type { i32, void ()*, i8* } // Create type { i32, void ()*, i8* }
val kCtorType = llvm.structType(llvm.int32Type, pointerType(kVoidFuncType), llvm.int8PtrType) val kCtorType = llvm.structType(llvm.int32Type, pointerType(ctorFunctionSignature.llvmFunctionType), llvm.int8PtrType)
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
// Create object { i32, void ()*, i8* } { i32 1, void ()* @ctorFunction, i8* null } // Create object { i32, void ()*, i8* } { i32 1, void ()* @ctorFunction, i8* null }
fun createGlobalCtor(ctorFunction: LLVMValueRef): ConstPointer { fun createGlobalCtor(ctorFunction: LlvmCallable): ConstPointer {
val priority = if (context.config.target.family == Family.MINGW) { val priority = if (context.config.target.family == Family.MINGW) {
// Workaround MinGW bug. Using this value makes the compiler generate // Workaround MinGW bug. Using this value makes the compiler generate
// '.ctors' section instead of '.ctors.XXXXX', which can't be recognized by ld // '.ctors' section instead of '.ctors.XXXXX', which can't be recognized by ld
@@ -2777,7 +2767,7 @@ internal class CodeGeneratorVisitor(
llvm.kImmInt32One llvm.kImmInt32One
} }
val data = llvm.kNullInt8Ptr val data = llvm.kNullInt8Ptr
val argList = cValuesOf(priority, ctorFunction, data) val argList = cValuesOf(priority, ctorFunction.toConstPointer().llvm, data)
val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!! val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!!
return constPointer(ctorItem) return constPointer(ctorItem)
} }
@@ -2787,7 +2777,7 @@ internal class CodeGeneratorVisitor(
// Note: the list of libraries is topologically sorted (in order for initializers to be called correctly). // Note: the list of libraries is topologically sorted (in order for initializers to be called correctly).
val dependencies = (generationState.dependenciesTracker.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */) val dependencies = (generationState.dependenciesTracker.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */)
val libraryToInitializers = dependencies.associate { it?.library to mutableListOf<LLVMValueRef>() } val libraryToInitializers = dependencies.associate { it?.library to mutableListOf<LlvmCallable>() }
llvm.irStaticInitializers.forEach { llvm.irStaticInitializers.forEach {
val library = it.konanLibrary val library = it.konanLibrary
@@ -2799,13 +2789,9 @@ internal class CodeGeneratorVisitor(
fun fileCtorName(libraryName: String, fileName: String) = "$libraryName:$fileName".moduleConstructorName fun fileCtorName(libraryName: String, fileName: String) = "$libraryName:$fileName".moduleConstructorName
fun addCtorFunction(ctorName: String) = fun ctorProto(ctorName: String): LlvmFunctionProto {
addLlvmFunctionWithDefaultAttributes( return ctorFunctionSignature.toProto(ctorName, null, LLVMLinkage.LLVMExternalLinkage)
generationState.context, }
llvm.module,
ctorName,
kVoidFuncType
).also { LLVMSetLinkage(it, LLVMLinkage.LLVMExternalLinkage) }
val ctorFunctions = dependencies.flatMap { dependency -> val ctorFunctions = dependencies.flatMap { dependency ->
val library = dependency?.library val library = dependency?.library
@@ -2823,10 +2809,9 @@ internal class CodeGeneratorVisitor(
if (library == null || generationState.llvmModuleSpecification.containsLibrary(library)) { if (library == null || generationState.llvmModuleSpecification.containsLibrary(library)) {
val otherInitializers = llvm.otherStaticInitializers.takeIf { library == null }.orEmpty() val otherInitializers = llvm.otherStaticInitializers.takeIf { library == null }.orEmpty()
val ctorFunction = addCtorFunction(ctorName) listOf(
appendStaticInitializers(ctorFunction, initializers + otherInitializers) appendStaticInitializers(ctorProto(ctorName), initializers + otherInitializers)
)
listOf(ctorFunction)
} else { } else {
// A cached library. // A cached library.
check(initializers.isEmpty()) { check(initializers.isEmpty()) {
@@ -2837,7 +2822,7 @@ internal class CodeGeneratorVisitor(
?: error("Library ${library.libraryFile} is expected to be cached") ?: error("Library ${library.libraryFile} is expected to be cached")
when (cache) { when (cache) {
is CachedLibraries.Cache.Monolithic -> listOf(addCtorFunction(ctorName)) is CachedLibraries.Cache.Monolithic -> listOf(ctorProto(ctorName))
is CachedLibraries.Cache.PerFile -> { is CachedLibraries.Cache.PerFile -> {
val files = when (dependency.kind) { val files = when (dependency.kind) {
is DependenciesTracker.DependencyKind.WholeModule -> is DependenciesTracker.DependencyKind.WholeModule ->
@@ -2845,8 +2830,10 @@ internal class CodeGeneratorVisitor(
is DependenciesTracker.DependencyKind.CertainFiles -> is DependenciesTracker.DependencyKind.CertainFiles ->
dependency.kind.files dependency.kind.files
} }
files.map { addCtorFunction(fileCtorName(library.uniqueName, it)) } files.map { ctorProto(fileCtorName(library.uniqueName, it)) }
} }
}.map {
codegen.addFunction(it)
} }
} }
} }
@@ -2854,9 +2841,9 @@ internal class CodeGeneratorVisitor(
appendGlobalCtors(ctorFunctions) appendGlobalCtors(ctorFunctions)
} }
private fun appendStaticInitializers(ctorFunction: LLVMValueRef, initializers: List<LLVMValueRef>) { private fun appendStaticInitializers(ctorCallableProto: LlvmFunctionProto, initializers: List<LlvmCallable>) : LlvmCallable {
generateFunctionNoRuntime(codegen, ctorFunction) { return generateFunctionNoRuntime(codegen, ctorCallableProto) {
val initGuardName = ctorFunction.name.orEmpty() + "_guard" val initGuardName = function.name.orEmpty() + "_guard"
val initGuard = LLVMAddGlobal(llvm.module, llvm.int32Type, initGuardName) val initGuard = LLVMAddGlobal(llvm.module, llvm.int32Type, initGuardName)
LLVMSetInitializer(initGuard, llvm.kImmInt32Zero) LLVMSetInitializer(initGuard, llvm.kImmInt32Zero)
LLVMSetLinkage(initGuard, LLVMLinkage.LLVMPrivateLinkage) LLVMSetLinkage(initGuard, LLVMLinkage.LLVMPrivateLinkage)
@@ -2884,27 +2871,30 @@ internal class CodeGeneratorVisitor(
} }
} }
private fun appendGlobalCtors(ctorFunctions: List<LLVMValueRef>) { private fun appendGlobalCtors(ctorFunctions: List<LlvmCallable>) {
if (context.config.isFinalBinary) { if (context.config.isFinalBinary) {
// Generate function calling all [ctorFunctions]. // Generate function calling all [ctorFunctions].
val globalCtorFunction = generateFunctionNoRuntime(codegen, kVoidFuncType, "_Konan_constructors") { val ctorProto = ctorFunctionSignature.toProto(
name = "_Konan_constructors",
origin = null,
linkage = if (context.config.produce == CompilerOutputKind.PROGRAM) LLVMLinkage.LLVMExternalLinkage else LLVMLinkage.LLVMPrivateLinkage
)
val globalCtorCallable = generateFunctionNoRuntime(codegen, ctorProto) {
ctorFunctions.forEach { ctorFunctions.forEach {
call(it, emptyList(), Lifetime.IRRELEVANT, call(it, emptyList(), Lifetime.IRRELEVANT,
exceptionHandler = ExceptionHandler.Caller, verbatim = true) exceptionHandler = ExceptionHandler.Caller, verbatim = true)
} }
ret(null) ret(null)
} }
LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMPrivateLinkage)
// Append initializers of global variables in "llvm.global_ctors" array. // Append initializers of global variables in "llvm.global_ctors" array.
val globalCtors = codegen.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, val globalCtors = codegen.staticData.placeGlobalArray("llvm.global_ctors", kCtorType,
listOf(createGlobalCtor(globalCtorFunction))) listOf(createGlobalCtor(globalCtorCallable)))
LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage)
if (context.config.produce == CompilerOutputKind.PROGRAM) { if (context.config.produce == CompilerOutputKind.PROGRAM) {
// Provide an optional handle for calling .ctors, if standard constructors mechanism // Provide an optional handle for calling .ctors, if standard constructors mechanism
// is not available on the platform (i.e. WASM, embedded). // is not available on the platform (i.e. WASM, embedded).
LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMExternalLinkage) appendLlvmUsed("llvm.used", listOf(globalCtorCallable.toConstPointer().llvm))
appendLlvmUsed("llvm.used", listOf(globalCtorFunction))
} }
} }
} }
@@ -87,7 +87,7 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native
.distinctBy { it.selector } .distinctBy { it.selector }
allInitMethodsInfo.mapTo(this) { allInitMethodsInfo.mapTo(this) {
ObjCMethodDesc(it.selector, it.encoding, llvm.missingInitImp.llvmValue) ObjCMethodDesc(it.selector, it.encoding, llvm.missingInitImp.toConstPointer())
} }
} }
@@ -108,10 +108,10 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native
private val impType = pointerType(functionType(llvm.int8PtrType, true, llvm.int8PtrType, llvm.int8PtrType)) private val impType = pointerType(functionType(llvm.int8PtrType, true, llvm.int8PtrType, llvm.int8PtrType))
private inner class ObjCMethodDesc( private inner class ObjCMethodDesc(
val selector: String, val encoding: String, val impFunction: LLVMValueRef val selector: String, val encoding: String, val impFunction: ConstPointer
) : Struct( ) : Struct(
runtime.objCMethodDescription, runtime.objCMethodDescription,
constPointer(impFunction).bitcast(impType), impFunction.bitcast(impType),
staticData.cStringLiteral(selector), staticData.cStringLiteral(selector),
staticData.cStringLiteral(encoding) staticData.cStringLiteral(encoding)
) )
@@ -126,7 +126,7 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native
ObjCMethodDesc( ObjCMethodDesc(
annotation.getAnnotationStringValue("selector"), annotation.getAnnotationStringValue("selector"),
annotation.getAnnotationStringValue("encoding"), annotation.getAnnotationStringValue("encoding"),
it.llvmFunction.llvmValue it.llvmFunction.toConstPointer()
) )
} }
@@ -136,16 +136,19 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native
Zero(runtime.kotlinObjCClassData) Zero(runtime.kotlinObjCClassData)
).pointer ).pointer
val functionType = functionType(classDataPointer.llvmType, false, llvm.int8PtrType, llvm.int8PtrType) val functionProto = LlvmFunctionSignature(
val functionName = "kobjcclassdataimp:${irClass.fqNameForIrSerialization}#internal" returnType = LlvmRetType(classDataPointer.llvmType),
parameterTypes = listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)),
val function = generateFunctionNoRuntime(codegen, functionType, functionName) { ).toProto(
name = "kobjcclassdataimp:${irClass.fqNameForIrSerialization}#internal",
origin = null,
LLVMLinkage.LLVMPrivateLinkage
)
val functionCallable = generateFunctionNoRuntime(codegen, functionProto) {
ret(classDataPointer.llvm) ret(classDataPointer.llvm)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
} }
return constPointer(function) return functionCallable.toConstPointer()
} }
private val codegen = CodeGenerator(generationState) private val codegen = CodeGenerator(generationState)
@@ -11,21 +11,12 @@ import org.jetbrains.kotlin.konan.target.Architecture
import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
internal fun addLlvmFunctionWithDefaultAttributes(
context: Context,
module: LLVMModuleRef,
name: String,
type: LLVMTypeRef
): LLVMValueRef = LLVMAddFunction(module, name, type)!!.also {
addDefaultLlvmFunctionAttributes(context, it)
addTargetCpuAndFeaturesAttributes(context, it)
}
/** /**
* Mimics parts of clang's `CodeGenModule::getDefaultFunctionAttributes` * Mimics parts of clang's `CodeGenModule::getDefaultFunctionAttributes`
* that are required for Kotlin/Native compiler. * that are required for Kotlin/Native compiler.
*/ */
private fun addDefaultLlvmFunctionAttributes(context: Context, llvmFunction: LLVMValueRef) { internal fun addDefaultLlvmFunctionAttributes(context: Context, llvmFunction: LLVMValueRef) {
if (shouldEnforceFramePointer(context)) { if (shouldEnforceFramePointer(context)) {
// Note: this is default for clang on at least on iOS and macOS. // Note: this is default for clang on at least on iOS and macOS.
enforceFramePointer(llvmFunction, context) enforceFramePointer(llvmFunction, context)
@@ -35,7 +26,7 @@ private fun addDefaultLlvmFunctionAttributes(context: Context, llvmFunction: LLV
/** /**
* Set target cpu and its features to make LLVM generate correct machine code. * Set target cpu and its features to make LLVM generate correct machine code.
*/ */
private fun addTargetCpuAndFeaturesAttributes(context: Context, llvmFunction: LLVMValueRef) { internal fun addTargetCpuAndFeaturesAttributes(context: Context, llvmFunction: LLVMValueRef) {
context.config.platform.targetCpu?.let { context.config.platform.targetCpu?.let {
LLVMAddTargetDependentFunctionAttr(llvmFunction, "target-cpu", it) LLVMAddTargetDependentFunctionAttr(llvmFunction, "target-cpu", it)
} }
@@ -5,19 +5,85 @@
package org.jetbrains.kotlin.backend.konan.llvm package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMGetReturnType import kotlinx.cinterop.toCValues
import llvm.LLVMTypeRef import llvm.*
import llvm.LLVMValueRef
/** /**
* Wrapper around LLVM value of functional type. * Wrapper around LLVM value of functional type.
*
* @todo This class mixes "something that can be called" and function abstractions.
* Some of it's methods make sense only for functions. Probably, LlvmFunction sub-class should be extracted.
*/ */
class LlvmCallable(val llvmValue: LLVMValueRef, val attributeProvider: LlvmFunctionAttributeProvider) { class LlvmCallable(private val llvmValue: LLVMValueRef, private val attributeProvider: LlvmFunctionAttributeProvider) {
val returnType: LLVMTypeRef by lazy { val returnType: LLVMTypeRef by lazy {
LLVMGetReturnType(functionType)!! LLVMGetReturnType(functionType)!!
} }
val name by lazy {
llvmValue.name
}
val functionType: LLVMTypeRef by lazy { val functionType: LLVMTypeRef by lazy {
getFunctionType(llvmValue) getFunctionType(llvmValue)
} }
val numParams by lazy {
LLVMCountParams(llvmValue)
}
val pgoFunctionNameVar by lazy {
LLVMCreatePGOFunctionNameVar(llvmValue, name)!!
}
val isConstant by lazy {
LLVMIsConstant(llvmValue) == 1
}
fun buildCall(builder: LLVMBuilderRef, args: List<LLVMValueRef>, name: String = "") =
LLVMBuildCall(builder, llvmValue, args.toCValues(), args.size, name)!!.also {
attributeProvider.addCallSiteAttributes(it)
}
fun buildInvoke(builder: LLVMBuilderRef, args: List<LLVMValueRef>, success: LLVMBasicBlockRef, catch: LLVMBasicBlockRef, name: String = "") =
LLVMBuildInvoke(builder, llvmValue, args.toCValues(), args.size, success, catch, name)!!.also {
attributeProvider.addCallSiteAttributes(it)
}
fun buildLandingpad(builder: LLVMBuilderRef, landingpadType: LLVMTypeRef, numClauses: Int, name: String = "") =
LLVMBuildLandingPad(builder, landingpadType, llvmValue, numClauses, name)!!
fun addBasicBlock(context: LLVMContextRef, name: String = "") =
LLVMAppendBasicBlockInContext(context, llvmValue, name)!!
fun blockAddress(label: LLVMBasicBlockRef) = LLVMBlockAddress(llvmValue, label)!!
fun addDebugInfoSubprogram(subprogram: DISubprogramRef) {
DIFunctionAddSubprogram(llvmValue, subprogram)
}
fun createBridgeFunctionDebugInfo(builder: DIBuilderRef, scope: DIScopeOpaqueRef, file: DIFileRef, lineNo: Int, type: DISubroutineTypeRef, isLocal: Int, isDefinition: Int, scopeLine: Int) =
DICreateBridgeFunction(
builder = builder,
scope = scope,
function = llvmValue,
file = file,
lineNo = lineNo,
type = type,
isLocal = isLocal,
isDefinition = isDefinition,
scopeLine = scopeLine
)!!
fun param(i: Int) : LLVMValueRef {
require(i in 0 until numParams)
return LLVMGetParam(llvmValue, i)!!
}
val isNoUnwind by lazy {
LLVMIsAFunction(llvmValue) != null && isFunctionNoUnwind(llvmValue)
}
// these functions are potentially unsafe, as they need to use same attribute provider when converted to callable
internal fun toConstPointer() = constPointer(llvmValue)
internal fun asCallback() = llvmValue
} }
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
import org.jetbrains.kotlin.backend.konan.descriptors.requiredAlignment import org.jetbrains.kotlin.backend.konan.descriptors.requiredAlignment
import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.lower.isStaticInitializer
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
@@ -352,10 +354,6 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
if (!declaration.isReal) return if (!declaration.isReal) return
if ((declaration is IrConstructor && declaration.isObjCConstructor)) {
return
}
val llvmFunction = if (declaration.isExternal) { val llvmFunction = if (declaration.isExternal) {
if (declaration.isTypedIntrinsic || declaration.isObjCBridgeBased() if (declaration.isTypedIntrinsic || declaration.isObjCBridgeBased()
// All call-sites to external accessors to interop properties // All call-sites to external accessors to interop properties
@@ -363,9 +361,12 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
|| (declaration.isAccessor && declaration.isFromInteropLibrary()) || (declaration.isAccessor && declaration.isFromInteropLibrary())
|| declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return || declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return
val proto = LlvmFunctionProto(declaration, declaration.computeSymbolName(), this) val proto = LlvmFunctionProto(declaration, declaration.computeSymbolName(), this, LLVMLinkage.LLVMExternalLinkage)
llvm.externalFunction(proto) llvm.externalFunction(proto)
} else { } else {
if (!declaration.shouldGenerateBody()) {
return
}
val symbolName = if (declaration.isExported()) { val symbolName = if (declaration.isExported()) {
declaration.computeSymbolName().also { declaration.computeSymbolName().also {
if (declaration.name.asString() != "main") { if (declaration.name.asString() != "main") {
@@ -385,16 +386,13 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG
} }
} }
val proto = LlvmFunctionProto(declaration, symbolName, this) val linkage = when {
val llvmFunction = addLlvmFunctionWithDefaultAttributes( declaration.isExported() -> LLVMLinkage.LLVMExternalLinkage
context, context.config.producePerFileCache && declaration in generationState.calledFromExportedInlineFunctions -> LLVMLinkage.LLVMExternalLinkage
llvm.module, else -> LLVMLinkage.LLVMInternalLinkage
symbolName,
proto.llvmFunctionType
).also {
proto.addFunctionAttributes(it)
} }
LlvmCallable(llvmFunction, proto) val proto = LlvmFunctionProto(declaration, symbolName, this, linkage)
proto.createLlvmFunction(context, llvm.module)
} }
declaration.metadata = KonanMetadata.Function(declaration, llvmFunction) declaration.metadata = KonanMetadata.Function(declaration, llvmFunction)
@@ -158,31 +158,35 @@ sealed class FunctionOrigin {
* Prototype of a LLVM function that is not tied to a specific LLVM module. * Prototype of a LLVM function that is not tied to a specific LLVM module.
*/ */
internal class LlvmFunctionProto( internal class LlvmFunctionProto(
val name: String, val name: String,
returnType: LlvmRetType, val signature: LlvmFunctionSignature,
parameterTypes: List<LlvmParamType> = emptyList(), val origin: FunctionOrigin?,
functionAttributes: List<LlvmFunctionAttribute> = emptyList(), val linkage: LLVMLinkage,
val origin: FunctionOrigin, val independent: Boolean = false,
isVararg: Boolean = false, ) {
val independent: Boolean = false, constructor(irFunction: IrFunction, symbolName: String, contextUtils: ContextUtils, linkage: LLVMLinkage) : this(
) : LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes) {
constructor(
name: String,
signature: LlvmFunctionSignature,
origin: FunctionOrigin,
independent: Boolean = false,
) : this(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, origin, signature.isVararg, independent)
constructor(irFunction: IrFunction, symbolName: String, contextUtils: ContextUtils) : this(
name = symbolName, name = symbolName,
returnType = contextUtils.getLlvmFunctionReturnType(irFunction), signature = LlvmFunctionSignature(irFunction, contextUtils),
parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction),
functionAttributes = inferFunctionAttributes(contextUtils, irFunction),
origin = FunctionOrigin.OwnedBy(irFunction), origin = FunctionOrigin.OwnedBy(irFunction),
linkage = linkage,
independent = irFunction.hasAnnotation(RuntimeNames.independent) independent = irFunction.hasAnnotation(RuntimeNames.independent)
) )
fun createLlvmFunction(context: Context, llvmModule: LLVMModuleRef): LlvmCallable {
val function = LLVMAddFunction(llvmModule, name, signature.llvmFunctionType)!!
addDefaultLlvmFunctionAttributes(context, function)
addTargetCpuAndFeaturesAttributes(context, function)
signature.addFunctionAttributes(function)
LLVMSetLinkage(function, linkage)
return LlvmCallable(function, signature)
}
} }
internal fun LlvmFunctionSignature.toProto(name: String, origin: FunctionOrigin?, linkage: LLVMLinkage, independent: Boolean = false) =
LlvmFunctionProto(name, this, origin, linkage, independent)
private fun mustNotInline(context: Context, irFunction: IrFunction): Boolean { private fun mustNotInline(context: Context, irFunction: IrFunction): Boolean {
if (context.shouldContainLocationDebugInfo()) { if (context.shouldContainLocationDebugInfo()) {
if (irFunction is IrConstructor && irFunction.isPrimary && irFunction.returnType.isThrowable()) { if (irFunction is IrConstructor && irFunction.isPrimary && irFunction.returnType.isThrowable()) {
@@ -126,12 +126,6 @@ fun extractConstUnsignedInt(value: LLVMValueRef): Long {
return LLVMConstIntGetZExtValue(value) return LLVMConstIntGetZExtValue(value)
} }
internal fun ContextUtils.isObjectReturn(functionType: LLVMTypeRef) : Boolean {
// Note that type is usually function pointer, so we have to dereference it.
val returnType = LLVMGetReturnType(LLVMGetElementType(functionType))!!
return isObjectType(returnType)
}
internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean { internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
return isObjectType(value.type) return isObjectType(value.type)
} }
@@ -194,11 +188,15 @@ private fun CodeGenerator.replaceExternalWeakOrCommonGlobal(name: String, value:
// When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well. // When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well.
// Runtime is linked into stdlib module only, so import runtime global from it. // Runtime is linked into stdlib module only, so import runtime global from it.
val global = importGlobal(name, value.llvmType) val global = importGlobal(name, value.llvmType)
val initializer = generateFunctionNoRuntime(this, functionType(llvm.voidType, false), "") { val initializerProto = LlvmFunctionSignature(LlvmRetType(llvm.voidType)).toProto(
name = "",
origin = null,
LLVMLinkage.LLVMPrivateLinkage
)
val initializer = generateFunctionNoRuntime(this, initializerProto) {
store(value.llvm, global) store(value.llvm, global)
ret(null) ret(null)
} }
LLVMSetLinkage(initializer, LLVMLinkage.LLVMPrivateLinkage)
llvm.otherStaticInitializers += initializer llvm.otherStaticInitializers += initializer
} else { } else {
@@ -262,7 +262,7 @@ internal class RTTIGenerator(
llvmDeclarations.writableTypeInfoGlobal?.pointer, llvmDeclarations.writableTypeInfoGlobal?.pointer,
associatedObjects = genAssociatedObjects(irClass), associatedObjects = genAssociatedObjects(irClass),
processObjectInMark = when { processObjectInMark = when {
irClass.symbol == context.ir.symbols.array -> constPointer(llvm.Kotlin_processArrayInMark.llvmValue) irClass.symbol == context.ir.symbols.array -> llvm.Kotlin_processArrayInMark.toConstPointer()
else -> genProcessObjectInMark(bodyType) else -> genProcessObjectInMark(bodyType)
}, },
requiredAlignment = llvmDeclarations.alignment requiredAlignment = llvmDeclarations.alignment
@@ -469,9 +469,9 @@ internal class RTTIGenerator(
val associatedObjectTableRecords = associatedObjects.map { (key, value) -> val associatedObjectTableRecords = associatedObjects.map { (key, value) ->
val function = context.getObjectClassInstanceFunction(value) val function = context.getObjectClassInstanceFunction(value)
val llvmFunction = generationState.llvmDeclarations.forFunction(function).llvmValue val llvmFunction = generationState.llvmDeclarations.forFunction(function)
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(llvmFunction)) Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, llvmFunction.toConstPointer())
} }
return staticData.placeGlobalConstArray( return staticData.placeGlobalConstArray(
@@ -486,11 +486,11 @@ internal class RTTIGenerator(
return when { return when {
indicesOfObjectFields.isEmpty() -> { indicesOfObjectFields.isEmpty() -> {
// TODO: Try to generate it here instead of importing from the runtime. // TODO: Try to generate it here instead of importing from the runtime.
constPointer(llvm.Kotlin_processEmptyObjectInMark.llvmValue) llvm.Kotlin_processEmptyObjectInMark.toConstPointer()
} }
else -> { else -> {
// TODO: specialize for "small" objects // TODO: specialize for "small" objects
constPointer(llvm.Kotlin_processObjectInMark.llvmValue) llvm.Kotlin_processObjectInMark.toConstPointer()
} }
} }
} }
@@ -6,7 +6,7 @@ package org.jetbrains.kotlin.backend.konan.llvm.coverage
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.llvm.LlvmCallable
import org.jetbrains.kotlin.backend.konan.reportCompilationError import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
@@ -88,7 +88,7 @@ internal class CoverageManager(val generationState: NativeGenerationState) {
/** /**
* @return [LLVMCoverageInstrumentation] instance if [irFunction] should be covered. * @return [LLVMCoverageInstrumentation] instance if [irFunction] should be covered.
*/ */
fun tryGetInstrumentation(irFunction: IrFunction?, callSitePlacer: (function: LLVMValueRef, args: List<LLVMValueRef>) -> Unit) = fun tryGetInstrumentation(irFunction: IrFunction?, callSitePlacer: (function: LlvmCallable, args: List<LLVMValueRef>) -> Unit) =
if (enabled && irFunction != null) { if (enabled && irFunction != null) {
getFunctionRegions(irFunction)?.let { LLVMCoverageInstrumentation(generationState, it, callSitePlacer) } getFunctionRegions(irFunction)?.let { LLVMCoverageInstrumentation(generationState, it, callSitePlacer) }
} else { } else {
@@ -21,13 +21,21 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
internal class LLVMCoverageInstrumentation( internal class LLVMCoverageInstrumentation(
override val generationState: NativeGenerationState, override val generationState: NativeGenerationState,
private val functionRegions: FunctionRegions, private val functionRegions: FunctionRegions,
private val callSitePlacer: (function: LLVMValueRef, args: List<LLVMValueRef>) -> Unit private val callSitePlacer: (function: LlvmCallable, args: List<LLVMValueRef>) -> Unit
) : ContextUtils { ) : ContextUtils {
private val functionNameGlobal = createFunctionNameGlobal(functionRegions.function) private val functionNameGlobal = createFunctionNameGlobal(functionRegions.function)
private val functionHash = llvm.int64(functionRegions.structuralHash) private val functionHash = llvm.int64(functionRegions.structuralHash)
private val instrProfIncrement by lazy {
val incrementFun = LLVMInstrProfIncrement(llvm.module)!!
LlvmCallable(
incrementFun,
LlvmFunctionAttributeProvider.copyFromExternal(incrementFun)
)
}
// TODO: It's a great place for some debug output. // TODO: It's a great place for some debug output.
fun instrumentIrElement(element: IrElement) { fun instrumentIrElement(element: IrElement) {
functionRegions.regions[element]?.let { functionRegions.regions[element]?.let {
@@ -42,13 +50,12 @@ internal class LLVMCoverageInstrumentation(
val numberOfRegions = llvm.int32(functionRegions.regions.size) val numberOfRegions = llvm.int32(functionRegions.regions.size)
val regionNumber = llvm.int32(functionRegions.regionEnumeration.getValue(region)) val regionNumber = llvm.int32(functionRegions.regionEnumeration.getValue(region))
val args = listOf(functionNameGlobal, functionHash, numberOfRegions, regionNumber) val args = listOf(functionNameGlobal, functionHash, numberOfRegions, regionNumber)
callSitePlacer(LLVMInstrProfIncrement(llvm.module)!!, args) callSitePlacer(instrProfIncrement, args)
} }
// Each profiled function should have a global with its name in a specific format. // Each profiled function should have a global with its name in a specific format.
private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef { private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef {
val name = function.llvmFunction.llvmValue.name val pgoFunctionName = function.llvmFunction.pgoFunctionNameVar
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction.llvmValue, name)!!
return LLVMConstBitCast(pgoFunctionName, llvm.int8PtrType)!! return LLVMConstBitCast(pgoFunctionName, llvm.int8PtrType)!!
} }
} }
@@ -54,7 +54,7 @@ internal class LLVMCoverageWriter(
fileIds.toCValues(), fileIds.size.signExtend(), fileIds.toCValues(), fileIds.size.signExtend(),
regions.toCValues(), regions.size.signExtend()) regions.toCValues(), regions.size.signExtend())
val functionName = generationState.llvmDeclarations.forFunction(functionRegions.function).llvmValue.name val functionName = generationState.llvmDeclarations.forFunction(functionRegions.function).name
val functionMappingRecord = LLVMAddFunctionMappingRecord(LLVMGetModuleContext(generationState.llvm.module), val functionMappingRecord = LLVMAddFunctionMappingRecord(LLVMGetModuleContext(generationState.llvm.module),
functionName, functionRegions.structuralHash, functionCoverage)!! functionName, functionRegions.structuralHash, functionCoverage)!!
@@ -27,14 +27,12 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
return load(classRef.llvm) return load(classRef.llvm)
} }
private val objcMsgSend = constPointer( private val objcMsgSend = llvm.externalNativeRuntimeFunction(
llvm.externalNativeRuntimeFunction(
"objc_msgSend", "objc_msgSend",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)),
isVararg = true isVararg = true
).llvmValue ).toConstPointer()
)
val objcRelease = llvm.externalNativeRuntimeFunction( val objcRelease = llvm.externalNativeRuntimeFunction(
"llvm.objc.release", "llvm.objc.release",
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter( internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
bridge: BlockPointerBridge bridge: BlockPointerBridge
): LLVMValueRef { ): LlvmCallable {
val irInterface = symbols.functionN(bridge.numberOfParameters).owner val irInterface = symbols.functionN(bridge.numberOfParameters).owner
val invokeMethod = irInterface.declarations.filterIsInstance<IrSimpleFunction>() val invokeMethod = irInterface.declarations.filterIsInstance<IrSimpleFunction>()
.single { it.name == OperatorNameConventions.INVOKE } .single { it.name == OperatorNameConventions.INVOKE }
@@ -33,8 +33,11 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
} }
val invokeImpl = functionGenerator( val invokeImpl = functionGenerator(
LlvmFunctionSignature(invokeMethod, codegen), LlvmFunctionSignature(invokeMethod, codegen).toProto(
"invokeFunction${bridge.nameSuffix}" "invokeFunction${bridge.nameSuffix}",
null,
LLVMLinkage.LLVMInternalLinkage
)
).generate { ).generate {
val thisRef = param(0) val thisRef = param(0)
val associatedObjectHolder = if (useSeparateHolder) { val associatedObjectHolder = if (useSeparateHolder) {
@@ -80,19 +83,17 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
.also { objcReleaseFromRunnableThreadState(result) } .also { objcReleaseFromRunnableThreadState(result) }
} }
ret(kotlinResult) ret(kotlinResult)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
val typeInfo = rttiGenerator.generateSyntheticInterfaceImpl( val typeInfo = rttiGenerator.generateSyntheticInterfaceImpl(
irInterface, irInterface,
mapOf(invokeMethod to constPointer(invokeImpl)), mapOf(invokeMethod to invokeImpl.toConstPointer()),
bodyType, bodyType,
immutable = true immutable = true
) )
val functionSig = LlvmFunctionSignature(LlvmRetType(codegen.kObjHeaderPtr), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(codegen.kObjHeaderPtrPtr)))
return functionGenerator( return functionGenerator(
LlvmFunctionSignature(LlvmRetType(codegen.kObjHeaderPtr), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(codegen.kObjHeaderPtrPtr))), functionSig.toProto("convertBlock${bridge.nameSuffix}", null, LLVMLinkage.LLVMInternalLinkage)
"convertBlock${bridge.nameSuffix}"
).generate { ).generate {
val blockPtr = param(0) val blockPtr = param(0)
ifThen(icmpEq(blockPtr, llvm.kNullInt8Ptr)) { ifThen(icmpEq(blockPtr, llvm.kNullInt8Ptr)) {
@@ -116,18 +117,17 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
} }
ret(result) ret(result)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
} }
private fun FunctionGenerationContext.loadBlockInvoke( private fun FunctionGenerationContext.loadBlockInvoke(
blockPtr: LLVMValueRef, blockPtr: LLVMValueRef,
bridge: BlockPointerBridge bridge: BlockPointerBridge
): LLVMValueRef { ): LlvmCallable {
val invokePtr = structGep(bitcast(pointerType(codegen.runtime.blockLiteralType), blockPtr), 3) val invokePtr = structGep(bitcast(pointerType(codegen.runtime.blockLiteralType), blockPtr), 3)
val signature = bridge.blockType.toBlockInvokeLlvmType(llvm)
return bitcast(pointerType(bridge.blockType.toBlockInvokeLlvmType(llvm).llvmFunctionType), load(invokePtr)) return LlvmCallable(bitcast(pointerType(signature.llvmFunctionType), load(invokePtr)), signature)
} }
private fun FunctionGenerationContext.allocInstanceWithAssociatedObject( private fun FunctionGenerationContext.allocInstanceWithAssociatedObject(
@@ -165,10 +165,18 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
codegen.runtime.kRefSharedHolderType codegen.runtime.kRefSharedHolderType
) )
val disposeProto = LlvmFunctionSignature(
LlvmRetType(llvm.voidType),
listOf(LlvmParamType(llvm.int8PtrType))
).toProto(
"blockDisposeHelper",
null,
LLVMLinkage.LLVMInternalLinkage
)
val disposeHelper = generateFunction( val disposeHelper = generateFunction(
codegen, codegen,
functionType(llvm.voidType, false, llvm.int8PtrType), disposeProto,
"blockDisposeHelper",
switchToRunnable = true switchToRunnable = true
) { ) {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
@@ -176,14 +184,20 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
call(llvm.kRefSharedHolderDispose, listOf(refHolder)) call(llvm.kRefSharedHolderDispose, listOf(refHolder))
ret(null) ret(null)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
val copyProto = LlvmFunctionSignature(
LlvmRetType(llvm.voidType),
listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType))
).toProto(
"blockCopyHelper",
null,
LLVMLinkage.LLVMInternalLinkage
)
val copyHelper = generateFunction( val copyHelper = generateFunction(
codegen, codegen,
functionType(llvm.voidType, false, llvm.int8PtrType, llvm.int8PtrType), copyProto,
"blockCopyHelper"
) { ) {
val dstBlockPtr = bitcast(pointerType(blockLiteralType), param(0)) val dstBlockPtr = bitcast(pointerType(blockLiteralType), param(0))
val dstRefHolder = structGep(dstBlockPtr, 1) val dstRefHolder = structGep(dstBlockPtr, 1)
@@ -204,8 +218,6 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
call(llvm.kRefSharedHolderInit, listOf(dstRefHolder, ref)) call(llvm.kRefSharedHolderInit, listOf(dstRefHolder, ref))
ret(null) ret(null)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
fun CodeGenerator.LongInt(value: Long) = fun CodeGenerator.LongInt(value: Long) =
@@ -236,8 +248,8 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
return Struct(codegen.runtime.blockDescriptorType, return Struct(codegen.runtime.blockDescriptorType,
codegen.LongInt(0L), codegen.LongInt(0L),
codegen.LongInt(LLVMStoreSizeOfType(codegen.runtime.targetData, blockLiteralType)), codegen.LongInt(LLVMStoreSizeOfType(codegen.runtime.targetData, blockLiteralType)),
constPointer(copyHelper), copyHelper.toConstPointer(),
constPointer(disposeHelper), disposeHelper.toConstPointer(),
codegen.staticData.cStringLiteral(signature), codegen.staticData.cStringLiteral(signature),
NullPointer(llvm.int8Type) NullPointer(llvm.int8Type)
) )
@@ -249,7 +261,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
invokeName: String, invokeName: String,
genBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit genBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit
): ConstPointer { ): ConstPointer {
val result = functionGenerator(blockType.toBlockInvokeLlvmType(llvm), invokeName) { val result = functionGenerator(blockType.toBlockInvokeLlvmType(llvm).toProto(invokeName, null, LLVMLinkage.LLVMInternalLinkage)) {
switchToRunnable = true switchToRunnable = true
}.generate { }.generate {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
@@ -263,16 +275,14 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
val arguments = (1 .. blockType.numberOfParameters).map { index -> param(index) } val arguments = (1 .. blockType.numberOfParameters).map { index -> param(index) }
genBody(kotlinObject, arguments) genBody(kotlinObject, arguments)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
return constPointer(result) return result.toConstPointer()
} }
fun ObjCExportCodeGeneratorBase.generateConvertFunctionToRetainedBlock( fun ObjCExportCodeGeneratorBase.generateConvertFunctionToRetainedBlock(
bridge: BlockPointerBridge bridge: BlockPointerBridge
): LLVMValueRef { ): LlvmCallable {
return generateWrapKotlinObjectToRetainedBlock( return generateWrapKotlinObjectToRetainedBlock(
bridge.blockType, bridge.blockType,
convertName = "convertFunction${bridge.nameSuffix}", convertName = "convertFunction${bridge.nameSuffix}",
@@ -299,15 +309,16 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
convertName: String, convertName: String,
invokeName: String, invokeName: String,
genBlockBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit genBlockBody: ObjCExportFunctionGenerationContext.(LLVMValueRef, List<LLVMValueRef>) -> Unit
): LLVMValueRef { ): LlvmCallable {
val blockDescriptor = codegen.staticData.placeGlobal( val blockDescriptor = codegen.staticData.placeGlobal(
"", "",
generateDescriptorForBlock(blockType) generateDescriptorForBlock(blockType)
) )
return functionGenerator( return functionGenerator(
LlvmFunctionSignature(LlvmRetType(llvm.int8PtrType), listOf(LlvmParamType(codegen.kObjHeaderPtr))), LlvmFunctionSignature(LlvmRetType(llvm.int8PtrType), listOf(LlvmParamType(codegen.kObjHeaderPtr))).toProto(
convertName convertName, null, LLVMLinkage.LLVMInternalLinkage
)
).generate { ).generate {
val kotlinRef = param(0) val kotlinRef = param(0)
ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) { ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) {
@@ -338,8 +349,6 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
val copiedBlock = callFromBridge(retainBlock, listOf(bitcast(llvm.int8PtrType, blockOnStack))) val copiedBlock = callFromBridge(retainBlock, listOf(bitcast(llvm.int8PtrType, blockOnStack)))
ret(copiedBlock) ret(copiedBlock)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
} }
} }
} }
@@ -111,19 +111,12 @@ internal class ObjCExportFunctionGenerationContext(
} }
internal class ObjCExportFunctionGenerationContextBuilder( internal class ObjCExportFunctionGenerationContextBuilder(
functionType: LlvmFunctionSignature, functionProto: LlvmFunctionProto,
functionName: String,
val objCExportCodegen: ObjCExportCodeGeneratorBase val objCExportCodegen: ObjCExportCodeGeneratorBase
) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>( ) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>(
functionType.llvmFunctionType, functionProto,
functionName,
objCExportCodegen.codegen objCExportCodegen.codegen
) { ) {
// Not a very pleasant way to provide attributes to the generated function.
init {
functionType.addFunctionAttributes(function)
}
// Unless specified otherwise, all generated bridges by ObjCExport should have `LeaveFrame` // Unless specified otherwise, all generated bridges by ObjCExport should have `LeaveFrame`
// because there is no guarantee of catching Kotlin exception in Kotlin code. // because there is no guarantee of catching Kotlin exception in Kotlin code.
var needCleanupLandingpadAndLeaveFrame = true var needCleanupLandingpadAndLeaveFrame = true
@@ -132,24 +125,22 @@ internal class ObjCExportFunctionGenerationContextBuilder(
} }
internal inline fun ObjCExportCodeGeneratorBase.functionGenerator( internal inline fun ObjCExportCodeGeneratorBase.functionGenerator(
functionType: LlvmFunctionSignature, functionProto: LlvmFunctionProto,
functionName: String,
configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {} configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {}
): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder( ): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder(
functionType, functionProto,
functionName,
this this
).apply(configure) ).apply(configure)
internal fun ObjCExportFunctionGenerationContext.callAndMaybeRetainAutoreleased( internal fun ObjCExportFunctionGenerationContext.callAndMaybeRetainAutoreleased(
function: LLVMValueRef, function: LlvmCallable,
signature: LlvmFunctionSignature, signature: LlvmFunctionSignature,
args: List<LLVMValueRef>, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT, resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler, exceptionHandler: ExceptionHandler,
doRetain: Boolean doRetain: Boolean
): LLVMValueRef { ): LLVMValueRef {
if (!doRetain) return call(function, args, resultLifetime, exceptionHandler, attributeProvider = signature) if (!doRetain) return call(function, args, resultLifetime, exceptionHandler)
// Objective-C runtime provides "optimizable" return for autoreleased references: // Objective-C runtime provides "optimizable" return for autoreleased references:
// the caller (this code) handles the return value with objc_retainAutoreleasedReturnValue, // the caller (this code) handles the return value with objc_retainAutoreleasedReturnValue,
@@ -170,15 +161,16 @@ internal fun ObjCExportFunctionGenerationContext.callAndMaybeRetainAutoreleased(
// and catch the exceptions in the caller of this function. // and catch the exceptions in the caller of this function.
// So, no exception handler in "outlined" => no redundant jump => the optimized return works properly. // So, no exception handler in "outlined" => no redundant jump => the optimized return works properly.
val functionIsPassedAsLastParameter = LLVMIsConstant(function) == 0 val functionIsPassedAsLastParameter = !function.isConstant
val valuesToPass = args + if (functionIsPassedAsLastParameter) listOf(function) else emptyList() val valuesToPass = args + if (functionIsPassedAsLastParameter) listOf(function.asCallback()) else emptyList()
val outlinedType = LlvmFunctionSignature( val outlinedType = LlvmFunctionSignature(
signature.returnType, signature.returnType,
signature.parameterTypes + if (functionIsPassedAsLastParameter) listOf(LlvmParamType(function.type)) else emptyList() signature.parameterTypes + if (functionIsPassedAsLastParameter) listOf(LlvmParamType(pointerType(function.functionType))) else emptyList(),
functionAttributes = listOf(LlvmFunctionAttribute.NoInline)
) )
val outlined = objCExportCodegen.functionGenerator(outlinedType, this.function.name.orEmpty() + "_outlined") { val outlined = objCExportCodegen.functionGenerator(outlinedType.toProto( this.function.name.orEmpty() + "_outlined", null, LLVMLinkage.LLVMPrivateLinkage)) {
setupBridgeDebugInfo() setupBridgeDebugInfo()
// Don't generate redundant cleanup landingpad (the generation would fail due to forbidRuntime below): // Don't generate redundant cleanup landingpad (the generation would fail due to forbidRuntime below):
needCleanupLandingpadAndLeaveFrame = false needCleanupLandingpadAndLeaveFrame = false
@@ -186,12 +178,10 @@ internal fun ObjCExportFunctionGenerationContext.callAndMaybeRetainAutoreleased(
forbidRuntime = true // Don't emit safe points, frame management etc. forbidRuntime = true // Don't emit safe points, frame management etc.
val actualArgs = signature.parameterTypes.indices.map { param(it) } val actualArgs = signature.parameterTypes.indices.map { param(it) }
val actualFunction = if (functionIsPassedAsLastParameter) param(signature.parameterTypes.size) else function val actualCallable = if (functionIsPassedAsLastParameter) LlvmCallable(param(signature.parameterTypes.size), signature) else function
// Use LLVMBuildCall instead of call, because the latter enforces using exception handler, which is exactly what we have to avoid. // Use LLVMBuildCall instead of call, because the latter enforces using exception handler, which is exactly what we have to avoid.
val result = LLVMBuildCall(builder, actualFunction, actualArgs.toCValues(), actualArgs.size, "")!!.also { val result = actualCallable.buildCall(builder, actualArgs).let { callResult ->
signature.addCallSiteAttributes(it)
}.let { callResult ->
// Simplified version of emitAutoreleasedReturnValueMarker in Clang: // Simplified version of emitAutoreleasedReturnValueMarker in Clang:
objCExportCodegen.objcRetainAutoreleasedReturnValueMarker?.let { objCExportCodegen.objcRetainAutoreleasedReturnValueMarker?.let {
LLVMBuildCall(arg0 = builder, Fn = it, Args = null, NumArgs = 0, Name = "") LLVMBuildCall(arg0 = builder, Fn = it, Args = null, NumArgs = 0, Name = "")
@@ -206,11 +196,8 @@ internal fun ObjCExportFunctionGenerationContext.callAndMaybeRetainAutoreleased(
ret(result) ret(result)
} }
outlinedType.addFunctionAttributes(outlined)
LLVMSetLinkage(outlined, LLVMLinkage.LLVMPrivateLinkage)
setFunctionNoInline(outlined)
return call(LlvmCallable(outlined, outlinedType), valuesToPass, resultLifetime, exceptionHandler) return call(outlined, valuesToPass, resultLifetime, exceptionHandler)
} }
internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCodeGenerator(codegen) { internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCodeGenerator(codegen) {
@@ -258,17 +245,17 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
fun ObjCExportFunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) = fun ObjCExportFunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) =
callFromBridge(llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime) callFromBridge(llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime)
private val blockToKotlinFunctionConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>() private val blockToKotlinFunctionConverterCache = mutableMapOf<BlockPointerBridge, LlvmCallable>()
internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LLVMValueRef = internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LlvmCallable =
blockToKotlinFunctionConverterCache.getOrPut(bridge) { blockToKotlinFunctionConverterCache.getOrPut(bridge) {
generateBlockToKotlinFunctionConverter(bridge) generateBlockToKotlinFunctionConverter(bridge)
} }
protected val blockGenerator = BlockGenerator(this.codegen) protected val blockGenerator = BlockGenerator(this.codegen)
private val functionToRetainedBlockConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>() private val functionToRetainedBlockConverterCache = mutableMapOf<BlockPointerBridge, LlvmCallable>()
internal fun kotlinFunctionToRetainedBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = internal fun kotlinFunctionToRetainedBlockConverter(bridge: BlockPointerBridge): LlvmCallable =
functionToRetainedBlockConverterCache.getOrPut(bridge) { functionToRetainedBlockConverterCache.getOrPut(bridge) {
blockGenerator.run { blockGenerator.run {
generateConvertFunctionToRetainedBlock(bridge) generateConvertFunctionToRetainedBlock(bridge)
@@ -312,13 +299,11 @@ internal class ObjCExportCodeGenerator(
val selectorsToDefine = mutableMapOf<String, MethodBridge>() val selectorsToDefine = mutableMapOf<String, MethodBridge>()
val externalGlobalInitializers = mutableMapOf<LLVMValueRef, ConstValue>() internal val continuationToRetainedCompletionConverter: LlvmCallable by lazy {
internal val continuationToRetainedCompletionConverter: LLVMValueRef by lazy {
generateContinuationToRetainedCompletionConverter(blockGenerator) generateContinuationToRetainedCompletionConverter(blockGenerator)
} }
internal val unitContinuationToRetainedCompletionConverter: LLVMValueRef by lazy { internal val unitContinuationToRetainedCompletionConverter: LlvmCallable by lazy {
generateUnitContinuationToRetainedCompletionConverter(blockGenerator) generateUnitContinuationToRetainedCompletionConverter(blockGenerator)
} }
@@ -564,15 +549,17 @@ internal class ObjCExportCodeGenerator(
) )
private fun emitSelectorsHolder() { private fun emitSelectorsHolder() {
val impType = functionType(llvm.voidType, false, llvm.int8PtrType, llvm.int8PtrType) val impProto = LlvmFunctionSignature(LlvmRetType(llvm.voidType)).toProto(
val imp = generateFunctionNoRuntime(codegen, impType, "") { name = "",
origin = null,
linkage = LLVMLinkage.LLVMInternalLinkage
)
val imp = generateFunctionNoRuntime(codegen, impProto) {
unreachable() unreachable()
} }
LLVMSetLinkage(imp, LLVMLinkage.LLVMInternalLinkage)
val methods = selectorsToDefine.map { (selector, bridge) -> val methods = selectorsToDefine.map { (selector, bridge) ->
ObjCDataGenerator.Method(selector, getEncoding(bridge), constPointer(imp)) ObjCDataGenerator.Method(selector, getEncoding(bridge), imp.toConstPointer())
} }
dataGenerator.emitClass( dataGenerator.emitClass(
@@ -754,7 +741,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
val boxClass = boxClassSymbol.owner val boxClass = boxClassSymbol.owner
val name = "${boxClass.name}ToNSNumber" val name = "${boxClass.name}ToNSNumber"
val converter = functionGenerator(kotlinToObjCFunctionType, name).generate { val converter = functionGenerator(kotlinToObjCFunctionType.toProto(name, null, LLVMLinkage.LLVMPrivateLinkage)).generate {
val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction val unboxFunction = context.getUnboxFunction(boxClass).llvmFunction
val kotlinValue = callFromBridge( val kotlinValue = callFromBridge(
unboxFunction, unboxFunction,
@@ -774,13 +761,12 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
ret(genSendMessage(returnType, valueParameterTypes, instance, nsNumberInitSelector, value)) ret(genSendMessage(returnType, valueParameterTypes, instance, nsNumberInitSelector, value))
} }
LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage) setObjCExportTypeInfo(boxClass, converter.toConstPointer())
setObjCExportTypeInfo(boxClass, constPointer(converter))
} }
private fun ObjCExportCodeGenerator.generateContinuationToRetainedCompletionConverter( private fun ObjCExportCodeGenerator.generateContinuationToRetainedCompletionConverter(
blockGenerator: BlockGenerator blockGenerator: BlockGenerator
): LLVMValueRef = with(blockGenerator) { ): LlvmCallable = with(blockGenerator) {
generateWrapKotlinObjectToRetainedBlock( generateWrapKotlinObjectToRetainedBlock(
BlockType(numberOfParameters = 2, returnsVoid = true), BlockType(numberOfParameters = 2, returnsVoid = true),
convertName = "convertContinuation", convertName = "convertContinuation",
@@ -798,7 +784,7 @@ private fun ObjCExportCodeGenerator.generateContinuationToRetainedCompletionConv
private fun ObjCExportCodeGenerator.generateUnitContinuationToRetainedCompletionConverter( private fun ObjCExportCodeGenerator.generateUnitContinuationToRetainedCompletionConverter(
blockGenerator: BlockGenerator blockGenerator: BlockGenerator
): LLVMValueRef = with(blockGenerator) { ): LlvmCallable = with(blockGenerator) {
generateWrapKotlinObjectToRetainedBlock( generateWrapKotlinObjectToRetainedBlock(
BlockType(numberOfParameters = 1, returnsVoid = true), BlockType(numberOfParameters = 1, returnsVoid = true),
convertName = "convertUnitContinuation", convertName = "convertUnitContinuation",
@@ -827,7 +813,7 @@ private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() {
mappedFunctionNClasses.forEach { functionClass -> mappedFunctionNClasses.forEach { functionClass ->
val convertToRetained = kotlinFunctionToRetainedBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false)) val convertToRetained = kotlinFunctionToRetainedBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false))
val writableTypeInfoValue = buildWritableTypeInfoValue(convertToRetained = constPointer(convertToRetained)) val writableTypeInfoValue = buildWritableTypeInfoValue(convertToRetained = convertToRetained.toConstPointer())
setOwnWritableTypeInfo(functionClass.irClass, writableTypeInfoValue) setOwnWritableTypeInfo(functionClass.irClass, writableTypeInfoValue)
} }
} }
@@ -841,7 +827,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() {
val converters = (0 until arityLimit).map { arity -> val converters = (0 until arityLimit).map { arity ->
functionClassesByArity[arity]?.let { functionClassesByArity[arity]?.let {
val bridge = BlockPointerBridge(numberOfParameters = arity, returnsVoid = false) val bridge = BlockPointerBridge(numberOfParameters = arity, returnsVoid = false)
constPointer(blockToKotlinFunctionConverter(bridge)) blockToKotlinFunctionConverter(bridge).toConstPointer()
} ?: NullPointer(objCToKotlinFunctionType) } ?: NullPointer(objCToKotlinFunctionType)
} }
@@ -859,7 +845,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() {
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
setObjCExportTypeInfo( setObjCExportTypeInfo(
symbols.string.owner, symbols.string.owner,
constPointer(llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString.llvmValue) llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString.toConstPointer()
) )
emitCollectionConverters() emitCollectionConverters()
@@ -870,7 +856,7 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
private fun ObjCExportCodeGenerator.emitCollectionConverters() { private fun ObjCExportCodeGenerator.emitCollectionConverters() {
fun importConverter(name: String): ConstPointer = fun importConverter(name: String): ConstPointer =
constPointer(llvm.externalNativeRuntimeFunction(name, kotlinToObjCFunctionType).llvmValue) llvm.externalNativeRuntimeFunction(name, kotlinToObjCFunctionType).toConstPointer()
setObjCExportTypeInfo( setObjCExportTypeInfo(
symbols.list.owner, symbols.list.owner,
@@ -914,10 +900,10 @@ private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
debugInfo: Boolean = false, debugInfo: Boolean = false,
suffix: String, suffix: String,
genBody: ObjCExportFunctionGenerationContext.() -> Unit genBody: ObjCExportFunctionGenerationContext.() -> Unit
): LLVMValueRef { ): LlvmCallable {
val functionType = objCFunctionType(generationState, methodBridge) val functionType = objCFunctionType(generationState, methodBridge)
val functionName = "objc2kotlin_$suffix" val functionName = "objc2kotlin_$suffix"
val result = functionGenerator(functionType, functionName) { val result = functionGenerator(functionType.toProto(functionName, null, LLVMLinkage.LLVMInternalLinkage)) {
if (debugInfo) { if (debugInfo) {
this.setupBridgeDebugInfo() this.setupBridgeDebugInfo()
} }
@@ -926,12 +912,10 @@ private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
}.generate { }.generate {
genBody() genBody()
} }
LLVMSetLinkage(result, LLVMLinkage.LLVMInternalLinkage)
return result return result
} }
private fun ObjCExportCodeGenerator.generateAbstractObjCImp(methodBridge: MethodBridge, baseMethod: IrFunction): LLVMValueRef = private fun ObjCExportCodeGenerator.generateAbstractObjCImp(methodBridge: MethodBridge, baseMethod: IrFunction): LlvmCallable =
generateObjCImpBy(methodBridge, suffix = baseMethod.computeSymbolName()) { generateObjCImpBy(methodBridge, suffix = baseMethod.computeSymbolName()) {
callFromBridge( callFromBridge(
llvm.Kotlin_ObjCExport_AbstractMethodCalled, llvm.Kotlin_ObjCExport_AbstractMethodCalled,
@@ -980,7 +964,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
resultLifetime: Lifetime, resultLifetime: Lifetime,
exceptionHandler: ExceptionHandler exceptionHandler: ExceptionHandler
) -> LLVMValueRef? ) -> LLVMValueRef?
): LLVMValueRef = generateObjCImpBy( ): LlvmCallable = generateObjCImpBy(
methodBridge, methodBridge,
debugInfo = isDirect /* see below */, debugInfo = isDirect /* see below */,
suffix = bridgeSuffix, suffix = bridgeSuffix,
@@ -1168,7 +1152,7 @@ private fun ObjCExportCodeGenerator.effectiveThrowsClasses(method: IrFunction, s
private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor( private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
target: IrConstructor, target: IrConstructor,
methodBridge: MethodBridge methodBridge: MethodBridge
): LLVMValueRef = generateObjCImp(methodBridge, bridgeSuffix = target.computeSymbolName(), isDirect = true) { args, resultLifetime, exceptionHandler -> ): LlvmCallable = generateObjCImp(methodBridge, bridgeSuffix = target.computeSymbolName(), isDirect = true) { args, resultLifetime, exceptionHandler ->
val arrayInstance = callFromBridge( val arrayInstance = callFromBridge(
llvm.allocArrayFunction, llvm.allocArrayFunction,
listOf(target.constructedClass.llvmTypeInfoPtr, args.first()), listOf(target.constructedClass.llvmTypeInfoPtr, args.first()),
@@ -1192,7 +1176,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val functionType = LlvmFunctionSignature(irFunction, codegen) val functionType = LlvmFunctionSignature(irFunction, codegen)
val functionName = "kotlin2objc_${baseIrFunction.computeSymbolName()}" val functionName = "kotlin2objc_${baseIrFunction.computeSymbolName()}"
val result = functionGenerator(functionType, functionName).generate { val result = functionGenerator(functionType.toProto(functionName, null, LLVMLinkage.LLVMInternalLinkage)).generate {
var errorOutPtr: LLVMValueRef? = null var errorOutPtr: LLVMValueRef? = null
val parameters = irFunction.allParameters.mapIndexed { index, parameterDescriptor -> val parameters = irFunction.allParameters.mapIndexed { index, parameterDescriptor ->
@@ -1286,7 +1270,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
// Using terminatingExceptionHandler, so any exception thrown by the method will lead to the termination, // Using terminatingExceptionHandler, so any exception thrown by the method will lead to the termination,
// and switching the thread state back to `Runnable` on exceptional path is not required. // and switching the thread state back to `Runnable` on exceptional path is not required.
val targetResult = callAndMaybeRetainAutoreleased( val targetResult = callAndMaybeRetainAutoreleased(
objcMsgSend.llvmValue, objcMsgSend,
objCFunctionType, objCFunctionType,
objCArgs, objCArgs,
exceptionHandler = terminatingExceptionHandler, exceptionHandler = terminatingExceptionHandler,
@@ -1403,9 +1387,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
ret(retVal) ret(retVal)
} }
LLVMSetLinkage(result, LLVMLinkage.LLVMInternalLinkage) return result.toConstPointer()
return constPointer(result)
} }
private fun MethodBridge.ReturnValue.isAutoreleasedObjCReference(): Boolean = when (this) { private fun MethodBridge.ReturnValue.isAutoreleasedObjCReference(): Boolean = when (this) {
@@ -1827,25 +1809,23 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
val functionType = objCFunctionType(generationState, methodBridge) val functionType = objCFunctionType(generationState, methodBridge)
val functionName = "objc2kotlin_$suffix" val functionName = "objc2kotlin_$suffix"
val imp = functionGenerator(functionType, functionName) { val imp = functionGenerator(functionType.toProto(functionName, null, LLVMLinkage.LLVMInternalLinkage)) {
switchToRunnable = true switchToRunnable = true
}.generate { }.generate {
block() block()
} }
LLVMSetLinkage(imp, LLVMLinkage.LLVMInternalLinkage)
return objCToKotlinMethodAdapter(selector, methodBridge, imp) return objCToKotlinMethodAdapter(selector, methodBridge, imp)
} }
private fun ObjCExportCodeGenerator.objCToKotlinMethodAdapter( private fun ObjCExportCodeGenerator.objCToKotlinMethodAdapter(
selector: String, selector: String,
methodBridge: MethodBridge, methodBridge: MethodBridge,
imp: LLVMValueRef imp: LlvmCallable
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
selectorsToDefine[selector] = methodBridge selectorsToDefine[selector] = methodBridge
return ObjCToKotlinMethodAdapter(selector, getEncoding(methodBridge), constPointer(imp)) return ObjCToKotlinMethodAdapter(selector, getEncoding(methodBridge), imp.toConstPointer())
} }
private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) = private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) =