[codegen][debug info] generating inline function information aka C 'inline' keyword (KT-30312)
This commit is contained in:
+7
-4
@@ -20,7 +20,8 @@ import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
|
||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
fun llvmFunction(function: IrFunction): LLVMValueRef = function.llvmFunction
|
||||
fun llvmFunction(function: IrFunction): LLVMValueRef = llvmFunctionOrNull(function) ?: error("no function ${function.name} in ${function.file}")
|
||||
fun llvmFunctionOrNull(function: IrFunction): LLVMValueRef? = function.llvmFunctionOrNull
|
||||
val intPtrType = LLVMIntPtrType(llvmTargetData)!!
|
||||
internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
|
||||
// Keep in sync with OBJECT_TAG_MASK in C++.
|
||||
@@ -83,9 +84,11 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
return typeInfoValue(constructedClass)
|
||||
}
|
||||
|
||||
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? {
|
||||
return LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column, locationInfo.scope)
|
||||
}
|
||||
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? = if (locationInfo.inlinedAt != null)
|
||||
LLVMCreateLocationInlinedAt(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column,
|
||||
locationInfo.scope, generateLocationInfo(locationInfo.inlinedAt))
|
||||
else
|
||||
LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column, locationInfo.scope)
|
||||
|
||||
val objCDataGenerator = when (context.config.target.family) {
|
||||
Family.IOS, Family.OSX -> ObjCDataGenerator(this)
|
||||
|
||||
+6
-1
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.DeserializedKonanModuleOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.fqNameSafe
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
||||
@@ -155,6 +157,9 @@ internal interface ContextUtils : RuntimeAware {
|
||||
* It may be declared as external function prototype.
|
||||
*/
|
||||
val IrFunction.llvmFunction: LLVMValueRef
|
||||
get() = llvmFunctionOrNull ?: error("$name in $file/${parent.fqNameSafe}")
|
||||
|
||||
val IrFunction.llvmFunctionOrNull: LLVMValueRef?
|
||||
get() {
|
||||
assert(this.isReal)
|
||||
|
||||
@@ -162,7 +167,7 @@ internal interface ContextUtils : RuntimeAware {
|
||||
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this),
|
||||
origin = this.llvmSymbolOrigin)
|
||||
} else {
|
||||
context.llvmDeclarations.forFunction(this).llvmFunction
|
||||
context.llvmDeclarations.forFunctionOrNull(this)?.llvmFunction
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -64,6 +64,8 @@ fun KonanConfig.debugInfoVersion():Int = configuration[KonanConfigKeys.DEBUG_INF
|
||||
internal class DebugInfo internal constructor(override val context: Context):ContextUtils {
|
||||
val files = mutableMapOf<String, DIFileRef>()
|
||||
val subprograms = mutableMapOf<LLVMValueRef, DISubprogramRef>()
|
||||
/* Some functions are inlined on all callsites and body is eliminated by DCE, so there's no LLVM value */
|
||||
val inlinedSubprograms = mutableMapOf<IrFunction, DISubprogramRef>()
|
||||
var builder: DIBuilderRef? = null
|
||||
var module: DIModuleRef? = null
|
||||
var types = mutableMapOf<KotlinType, DITypeOpaqueRef>()
|
||||
|
||||
+42
-18
@@ -1615,6 +1615,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
override fun returnableBlockScope(): CodeContext? = this
|
||||
|
||||
override fun location(line: Int, column: Int): LocationInfo? {
|
||||
return if (returnableBlock.inlineFunctionSymbol != null) {
|
||||
val diScope = returnableBlock.inlineFunctionSymbol?.owner?.scope() ?: return null
|
||||
LocationInfo(diScope, line, column, outerContext.location(returnableBlock.startLine(), returnableBlock.endLine()))
|
||||
} else {
|
||||
outerContext.location(line, column)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note: DILexicalBlocks aren't nested, they should be scoped with the parent function.
|
||||
@@ -1829,24 +1838,39 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
private fun IrFunction.scope(startLine:Int): DIScopeOpaqueRef? {
|
||||
if (codegen.isExternal(this) || !context.shouldContainDebugInfo())
|
||||
return null
|
||||
return context.debugInfo.subprograms.getOrPut(codegen.llvmFunction(this)) {
|
||||
memScoped {
|
||||
val subroutineType = subroutineType(context, codegen.llvmTargetData)
|
||||
val functionLlvmValue = codegen.llvmFunction(this@scope)
|
||||
diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType, functionLlvmValue)
|
||||
}
|
||||
} as DIScopeOpaqueRef
|
||||
val functionLlvmValue = codegen.llvmFunctionOrNull(this)
|
||||
return if (functionLlvmValue != null) {
|
||||
context.debugInfo.subprograms.getOrPut(functionLlvmValue) {
|
||||
memScoped {
|
||||
val subroutineType = subroutineType(context, codegen.llvmTargetData)
|
||||
val functionLlvmValue = codegen.llvmFunction(this@scope)
|
||||
diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType).also {
|
||||
DIFunctionAddSubprogram(functionLlvmValue, it)
|
||||
}
|
||||
}
|
||||
} as DIScopeOpaqueRef
|
||||
} else {
|
||||
context.debugInfo.inlinedSubprograms.getOrPut(this) {
|
||||
memScoped {
|
||||
val subroutineType = subroutineType(context, codegen.llvmTargetData)
|
||||
diFunctionScope(name.asString(), "<inlined-out:$name>", startLine, subroutineType)
|
||||
}
|
||||
} as DIScopeOpaqueRef
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef): DIScopeOpaqueRef? {
|
||||
return context.debugInfo.subprograms.getOrPut(this) {
|
||||
diFunctionScope(name!!, name!!, startLine, subroutineType, this)
|
||||
diFunctionScope(name!!, name!!, startLine, subroutineType).also {
|
||||
DIFunctionAddSubprogram(this@scope, it)
|
||||
}
|
||||
} as DIScopeOpaqueRef
|
||||
}
|
||||
private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef, functionLlvmValue: LLVMValueRef): DISubprogramRef {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val diFunction = DICreateFunction(
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef) = DICreateFunction(
|
||||
builder = context.debugInfo.builder,
|
||||
scope = (currentCodeContext.fileScope() as FileScope).file.file() as DIScopeOpaqueRef,
|
||||
name = name,
|
||||
@@ -1857,10 +1881,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
//TODO: need more investigations.
|
||||
isLocal = 0,
|
||||
isDefinition = 1,
|
||||
scopeLine = 0)
|
||||
DIFunctionAddSubprogram(functionLlvmValue, diFunction)
|
||||
return diFunction!!
|
||||
}
|
||||
scopeLine = 0)!!
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
@@ -2353,9 +2374,12 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
}
|
||||
|
||||
internal data class LocationInfo(val scope: DIScopeOpaqueRef,
|
||||
val line: Int,
|
||||
val column: Int) {
|
||||
|
||||
|
||||
internal class LocationInfo(val scope: DIScopeOpaqueRef,
|
||||
val line: Int,
|
||||
val column: Int,
|
||||
val inlinedAt: LocationInfo? = null) {
|
||||
init {
|
||||
assert(line != 0)
|
||||
}
|
||||
|
||||
+3
-2
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.types.isNothing
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.fqNameSafe
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -43,8 +44,8 @@ internal class LlvmDeclarations(
|
||||
private val fields: Map<IrField, FieldLlvmDeclarations>,
|
||||
private val staticFields: Map<IrField, StaticFieldLlvmDeclarations>,
|
||||
private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
|
||||
fun forFunction(function: IrFunction) = functions[function] ?:
|
||||
error("${function.descriptor} in ${function.parent.fqNameSafe}")
|
||||
fun forFunction(function: IrFunction) = forFunctionOrNull(function) ?: with(function){error("$name in $file/${parent.fqNameSafe}")}
|
||||
fun forFunctionOrNull(function: IrFunction) = functions[function]
|
||||
|
||||
fun forClass(irClass: IrClass) = classes[irClass] ?:
|
||||
error(irClass.descriptor.toString())
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ internal class LLVMCoverageInstrumentation(
|
||||
|
||||
// Each profiled function should have a global with its name in a specific format.
|
||||
private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef {
|
||||
val name = context.llvmDeclarations.forFunction(function).llvmFunction.name
|
||||
val name = function.llvmFunction.name
|
||||
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction, name)!!
|
||||
return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!!
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user