[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 {
|
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)!!
|
val intPtrType = LLVMIntPtrType(llvmTargetData)!!
|
||||||
internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
|
internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
|
||||||
// Keep in sync with OBJECT_TAG_MASK in C++.
|
// Keep in sync with OBJECT_TAG_MASK in C++.
|
||||||
@@ -83,9 +84,11 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
return typeInfoValue(constructedClass)
|
return typeInfoValue(constructedClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? {
|
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? = if (locationInfo.inlinedAt != null)
|
||||||
return LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column, locationInfo.scope)
|
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) {
|
val objCDataGenerator = when (context.config.target.family) {
|
||||||
Family.IOS, Family.OSX -> ObjCDataGenerator(this)
|
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.CurrentKonanModuleOrigin
|
||||||
import org.jetbrains.kotlin.descriptors.konan.DeserializedKonanModuleOrigin
|
import org.jetbrains.kotlin.descriptors.konan.DeserializedKonanModuleOrigin
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.ir.util.isEffectivelyExternal
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||||
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
||||||
@@ -155,6 +157,9 @@ internal interface ContextUtils : RuntimeAware {
|
|||||||
* It may be declared as external function prototype.
|
* It may be declared as external function prototype.
|
||||||
*/
|
*/
|
||||||
val IrFunction.llvmFunction: LLVMValueRef
|
val IrFunction.llvmFunction: LLVMValueRef
|
||||||
|
get() = llvmFunctionOrNull ?: error("$name in $file/${parent.fqNameSafe}")
|
||||||
|
|
||||||
|
val IrFunction.llvmFunctionOrNull: LLVMValueRef?
|
||||||
get() {
|
get() {
|
||||||
assert(this.isReal)
|
assert(this.isReal)
|
||||||
|
|
||||||
@@ -162,7 +167,7 @@ internal interface ContextUtils : RuntimeAware {
|
|||||||
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this),
|
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this),
|
||||||
origin = this.llvmSymbolOrigin)
|
origin = this.llvmSymbolOrigin)
|
||||||
} else {
|
} 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 {
|
internal class DebugInfo internal constructor(override val context: Context):ContextUtils {
|
||||||
val files = mutableMapOf<String, DIFileRef>()
|
val files = mutableMapOf<String, DIFileRef>()
|
||||||
val subprograms = mutableMapOf<LLVMValueRef, DISubprogramRef>()
|
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 builder: DIBuilderRef? = null
|
||||||
var module: DIModuleRef? = null
|
var module: DIModuleRef? = null
|
||||||
var types = mutableMapOf<KotlinType, DITypeOpaqueRef>()
|
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 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.
|
* 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? {
|
private fun IrFunction.scope(startLine:Int): DIScopeOpaqueRef? {
|
||||||
if (codegen.isExternal(this) || !context.shouldContainDebugInfo())
|
if (codegen.isExternal(this) || !context.shouldContainDebugInfo())
|
||||||
return null
|
return null
|
||||||
return context.debugInfo.subprograms.getOrPut(codegen.llvmFunction(this)) {
|
val functionLlvmValue = codegen.llvmFunctionOrNull(this)
|
||||||
memScoped {
|
return if (functionLlvmValue != null) {
|
||||||
val subroutineType = subroutineType(context, codegen.llvmTargetData)
|
context.debugInfo.subprograms.getOrPut(functionLlvmValue) {
|
||||||
val functionLlvmValue = codegen.llvmFunction(this@scope)
|
memScoped {
|
||||||
diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType, functionLlvmValue)
|
val subroutineType = subroutineType(context, codegen.llvmTargetData)
|
||||||
}
|
val functionLlvmValue = codegen.llvmFunction(this@scope)
|
||||||
} as DIScopeOpaqueRef
|
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")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef): DIScopeOpaqueRef? {
|
private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef): DIScopeOpaqueRef? {
|
||||||
return context.debugInfo.subprograms.getOrPut(this) {
|
return context.debugInfo.subprograms.getOrPut(this) {
|
||||||
diFunctionScope(name!!, name!!, startLine, subroutineType, this)
|
diFunctionScope(name!!, name!!, startLine, subroutineType).also {
|
||||||
|
DIFunctionAddSubprogram(this@scope, it)
|
||||||
|
}
|
||||||
} as DIScopeOpaqueRef
|
} as DIScopeOpaqueRef
|
||||||
}
|
}
|
||||||
private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef, functionLlvmValue: LLVMValueRef): DISubprogramRef {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val diFunction = DICreateFunction(
|
private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef) = DICreateFunction(
|
||||||
builder = context.debugInfo.builder,
|
builder = context.debugInfo.builder,
|
||||||
scope = (currentCodeContext.fileScope() as FileScope).file.file() as DIScopeOpaqueRef,
|
scope = (currentCodeContext.fileScope() as FileScope).file.file() as DIScopeOpaqueRef,
|
||||||
name = name,
|
name = name,
|
||||||
@@ -1857,10 +1881,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
//TODO: need more investigations.
|
//TODO: need more investigations.
|
||||||
isLocal = 0,
|
isLocal = 0,
|
||||||
isDefinition = 1,
|
isDefinition = 1,
|
||||||
scopeLine = 0)
|
scopeLine = 0)!!
|
||||||
DIFunctionAddSubprogram(functionLlvmValue, diFunction)
|
|
||||||
return diFunction!!
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
@@ -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 {
|
init {
|
||||||
assert(line != 0)
|
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.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameSafe
|
import org.jetbrains.kotlin.ir.util.fqNameSafe
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
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.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -43,8 +44,8 @@ internal class LlvmDeclarations(
|
|||||||
private val fields: Map<IrField, FieldLlvmDeclarations>,
|
private val fields: Map<IrField, FieldLlvmDeclarations>,
|
||||||
private val staticFields: Map<IrField, StaticFieldLlvmDeclarations>,
|
private val staticFields: Map<IrField, StaticFieldLlvmDeclarations>,
|
||||||
private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
|
private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
|
||||||
fun forFunction(function: IrFunction) = functions[function] ?:
|
fun forFunction(function: IrFunction) = forFunctionOrNull(function) ?: with(function){error("$name in $file/${parent.fqNameSafe}")}
|
||||||
error("${function.descriptor} in ${function.parent.fqNameSafe}")
|
fun forFunctionOrNull(function: IrFunction) = functions[function]
|
||||||
|
|
||||||
fun forClass(irClass: IrClass) = classes[irClass] ?:
|
fun forClass(irClass: IrClass) = classes[irClass] ?:
|
||||||
error(irClass.descriptor.toString())
|
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.
|
// 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 = context.llvmDeclarations.forFunction(function).llvmFunction.name
|
val name = function.llvmFunction.name
|
||||||
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction, name)!!
|
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction, name)!!
|
||||||
return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!!
|
return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!!
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user