[codegen][debug info] generating inline function information aka C 'inline' keyword (KT-30312)

This commit is contained in:
Vasily Levchenko
2019-04-04 16:25:55 +03:00
parent 7dead9657b
commit 2aa2bb45ba
6 changed files with 61 additions and 26 deletions
@@ -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)
@@ -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
}
}
@@ -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>()
@@ -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)
}
@@ -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())
@@ -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)!!
}