CODEGEN: generation type info for complex types and refactoring.
- unchecked_cast warning suppression - move subroutineType ext function to DebugUtils. - there is only one diType in caodegen - generate type references for complex types if they are references - generating array types
This commit is contained in:
committed by
vvlevchenko
parent
b1462f8da6
commit
9d63662fbe
+3
-13
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.DumpIrTreeWithDescriptorsVisitor
|
||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
@@ -268,6 +265,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
field = module!!
|
||||
|
||||
llvm = Llvm(this, module)
|
||||
debugInfo = DebugInfo(this)
|
||||
}
|
||||
|
||||
lateinit var llvm: Llvm
|
||||
@@ -419,14 +417,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
}
|
||||
}
|
||||
|
||||
val debugInfo = DebugInfo()
|
||||
class DebugInfo {
|
||||
val files = mutableMapOf<IrFile, DIFileRef>()
|
||||
val subprograms = mutableMapOf<FunctionDescriptor, DISubprogramRef>()
|
||||
var builder: DIBuilderRef? = null
|
||||
var module: DIModuleRef? = null
|
||||
var compilationModule: DICompileUnitRef? = null
|
||||
var types = mutableMapOf<KotlinType, DITypeOpaqueRef>()
|
||||
}
|
||||
lateinit var debugInfo:DebugInfo
|
||||
}
|
||||
|
||||
|
||||
+102
-7
@@ -16,19 +16,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.DICreateCompilationUnit
|
||||
import llvm.DICreateModule
|
||||
import llvm.DIScopeOpaqueRef
|
||||
import llvm.LLVMAddNamedMetadataOperand
|
||||
import kotlinx.cinterop.allocArrayOf
|
||||
import kotlinx.cinterop.memScoped
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.KonanVersion
|
||||
import org.jetbrains.kotlin.ir.SourceManager.FileEntry
|
||||
import org.jetbrains.kotlin.backend.konan.util.File
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.SourceManager.FileEntry
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
|
||||
internal object DWARF {
|
||||
val DW_LANG_kotlin = 1 //TODO: we need own constant e.g. 0xbabe
|
||||
val producer = "konanc ${KonanVersion.CURRENT} / kotlin-compiler: ${KotlinVersion.CURRENT}"
|
||||
/* TODO: from LLVM sources is unclear what runtimeVersion corresponds to term in terms of dwarf specification. */
|
||||
val runtimeVersion = 2
|
||||
@@ -38,7 +41,31 @@ internal object DWARF {
|
||||
val debugInfoVersion = 3 /* TODO: configurable? */
|
||||
}
|
||||
|
||||
internal class DebugInfo internal constructor(override val context: Context):ContextUtils {
|
||||
val files = mutableMapOf<IrFile, DIFileRef>()
|
||||
val subprograms = mutableMapOf<FunctionDescriptor, DISubprogramRef>()
|
||||
var builder: DIBuilderRef? = null
|
||||
var module: DIModuleRef? = null
|
||||
var compilationModule: DICompileUnitRef? = null
|
||||
var types = mutableMapOf<KotlinType, DITypeOpaqueRef>()
|
||||
|
||||
val llvmTypes = mapOf<KotlinType, LLVMTypeRef>(
|
||||
context.builtIns.byteType to LLVMInt8Type()!!,
|
||||
context.builtIns.charType to LLVMInt8Type()!!,
|
||||
context.builtIns.shortType to LLVMInt16Type()!!,
|
||||
context.builtIns.intType to LLVMInt32Type()!!,
|
||||
context.builtIns.longType to LLVMInt64Type()!!,
|
||||
context.builtIns.booleanType to LLVMInt1Type()!!,
|
||||
context.builtIns.floatType to LLVMFloatType()!!,
|
||||
context.builtIns.doubleType to LLVMDoubleType()!!)
|
||||
val intTypes = listOf<KotlinType>(context.builtIns.byteType, context.builtIns.shortType, context.builtIns.longType)
|
||||
val realTypes = listOf<KotlinType>(context.builtIns.floatType, context.builtIns.doubleType)
|
||||
val llvmTypeSizes = llvmTypes.map { it.key to LLVMSizeOfTypeInBits(llvmTargetData, it.value) }.toMap()
|
||||
val llvmTypeAlignments = llvmTypes.map {it.key to LLVMPreferredAlignmentOfType(llvmTargetData, it.value)}.toMap()
|
||||
val otherLlvmType = LLVMPointerType(LLVMInt64Type(), 0)!!
|
||||
val otherTypeSize = LLVMSizeOfTypeInBits(llvmTargetData, otherLlvmType)
|
||||
val otherTypeAlignment = LLVMPreferredAlignmentOfType(llvmTargetData, otherLlvmType)
|
||||
}
|
||||
/**
|
||||
* File entry starts offsets from zero while dwarf number lines/column starting from 1.
|
||||
*/
|
||||
@@ -77,7 +104,7 @@ internal fun generateDebugInfoHeader(context: Context) {
|
||||
iSysRoot = "")
|
||||
context.debugInfo.compilationModule = DICreateCompilationUnit(
|
||||
builder = context.debugInfo.builder,
|
||||
lang = DWARF.DW_LANG_kotlin,
|
||||
lang = DwarfLanguage.DW_LANG_Kotlin.value,
|
||||
File = path.file,
|
||||
dir = path.folder,
|
||||
producer = DWARF.producer,
|
||||
@@ -112,3 +139,71 @@ internal fun generateDebugInfoHeader(context: Context) {
|
||||
LLVMAddNamedMetadataOperand(context.llvmModule, llvmModuleFlags, nodeDebugInfoVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun KotlinType.dwarfType(context:Context, targetData:LLVMTargetDataRef): DITypeOpaqueRef {
|
||||
return when {
|
||||
KotlinBuiltIns.isPrimitiveType(this) -> debugInfoBaseType(context, targetData, this.getJetTypeFqName(false), llvmType(context), encoding(context).value.toInt())
|
||||
KotlinBuiltIns.isArray(this) -> {
|
||||
val arrayElementType = context.builtIns.getArrayElementType(this)
|
||||
DICreateArrayType(context.debugInfo.builder, arrayElementType.size(context), arrayElementType.alignment(context),
|
||||
arrayElementType.diType(context, targetData), 1) as DITypeOpaqueRef
|
||||
}
|
||||
else -> debugInfoBaseType(context, targetData, "Any?", llvmType(context), encoding(context).value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinType.diType(context: Context, llvmTargetData: LLVMTargetDataRef): DITypeOpaqueRef =
|
||||
context.debugInfo.types.getOrPut(this) {
|
||||
dwarfType(context, llvmTargetData)
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun debugInfoBaseType(context:Context, targetData:LLVMTargetDataRef, typeName:String, type:LLVMTypeRef, encoding:Int) = DICreateBasicType(
|
||||
context.debugInfo.builder, typeName,
|
||||
LLVMSizeOfTypeInBits(targetData, type),
|
||||
LLVMPreferredAlignmentOfType(targetData, type).toLong(), encoding) as DITypeOpaqueRef
|
||||
|
||||
internal val FunctionDescriptor.types:List<KotlinType>
|
||||
get() {
|
||||
val parameters = valueParameters.map{it.type}
|
||||
return if (returnType != null) listOf(returnType!!, *parameters.toTypedArray()) else parameters
|
||||
}
|
||||
|
||||
internal fun KotlinType.size(context:Context) = context.debugInfo.llvmTypeSizes.getOrDefault(this, context.debugInfo.otherTypeSize)
|
||||
|
||||
internal fun KotlinType.alignment(context:Context) = context.debugInfo.llvmTypeAlignments.getOrDefault(this, context.debugInfo.otherTypeAlignment).toLong()
|
||||
|
||||
internal fun KotlinType.llvmType(context:Context): LLVMTypeRef = context.debugInfo.llvmTypes.getOrDefault(this, context.debugInfo.otherLlvmType)
|
||||
|
||||
private fun<T> or(v:T, vararg p:(T)->Boolean):Boolean = p.any{it(v)}
|
||||
internal fun KotlinType.encoding(context:Context):DwarfTypeKind {
|
||||
return when {
|
||||
this in context.debugInfo.intTypes -> DwarfTypeKind.DW_ATE_signed
|
||||
this in context.debugInfo.realTypes -> DwarfTypeKind.DW_ATE_float
|
||||
KotlinBuiltIns.isBoolean(this) -> DwarfTypeKind.DW_ATE_boolean
|
||||
KotlinBuiltIns.isChar(this) -> DwarfTypeKind.DW_ATE_unsigned
|
||||
(!KotlinBuiltIns.isPrimitiveType(this)) -> DwarfTypeKind.DW_ATE_address
|
||||
else -> TODO(toString())
|
||||
|
||||
}
|
||||
}
|
||||
internal fun alignTo(value:Long, align:Long):Long = (value + align - 1) / align * align
|
||||
|
||||
internal fun FunctionDescriptor.subroutineType(context: Context, llvmTargetData: LLVMTargetDataRef): DISubroutineTypeRef {
|
||||
return memScoped {
|
||||
DICreateSubroutineType(context.debugInfo.builder, allocArrayOf(
|
||||
this@subroutineType.types.map { it.referenceOrValue(context, llvmTargetData) }),
|
||||
this@subroutineType.types.size)!!
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun KotlinType.referenceOrValue(context: Context, llvmTargetData: LLVMTargetDataRef):DITypeOpaqueRef {
|
||||
val refType = this.dwarfType(context, llvmTargetData)
|
||||
return if (KotlinBuiltIns.isPrimitiveType(this))
|
||||
refType
|
||||
else
|
||||
DICreateReferenceType(context.debugInfo.builder, refType) as DITypeOpaqueRef
|
||||
}
|
||||
|
||||
+90
-38
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
@@ -197,9 +196,17 @@ internal interface CodeContext {
|
||||
/**
|
||||
* Returns owning file scope.
|
||||
*
|
||||
* @return the requested value
|
||||
* @return the requested value if in the file scope or null.
|
||||
*/
|
||||
fun fileScope(): CodeContext?
|
||||
|
||||
/**
|
||||
* Returns owning class scope [ClassScope].
|
||||
*
|
||||
* @returns the requested value if in the class scope or null.
|
||||
*/
|
||||
fun classScope(): CodeContext?
|
||||
|
||||
fun addResumePoint(bbLabel: LLVMBasicBlockRef)
|
||||
}
|
||||
|
||||
@@ -243,7 +250,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
override fun functionScope(): CodeContext? = null
|
||||
|
||||
override fun fileScope(): CodeContext? = null
|
||||
|
||||
|
||||
override fun classScope(): CodeContext? = null
|
||||
|
||||
override fun addResumePoint(bbLabel: LLVMBasicBlockRef) = unsupported(bbLabel)
|
||||
}
|
||||
|
||||
@@ -575,8 +584,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
// do not generate any code for annotation classes as a workaround for NotImplementedError
|
||||
return
|
||||
}
|
||||
|
||||
declaration.acceptChildrenVoid(this)
|
||||
using(ClassScope(declaration)) {
|
||||
debugClassDeclaration(declaration) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -595,6 +607,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
override fun visitField(expression: IrField) {
|
||||
context.log{"visitField : ${ir2string(expression)}"}
|
||||
debugFieldDeclaration(expression)
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor.containingDeclaration is PackageFragmentDescriptor) {
|
||||
val type = codegen.getLLVMType(descriptor.type)
|
||||
@@ -1071,7 +1084,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val line = value.line()
|
||||
codegen.vars.debugInfoLocalVariableLocation(
|
||||
functionScope = functionScope,
|
||||
diType = variableDescriptor.diType,
|
||||
diType = variableDescriptor.type.diType(context, codegen.llvmTargetData),
|
||||
name = variableDescriptor.name,
|
||||
variable = variable,
|
||||
file = file,
|
||||
@@ -1395,6 +1408,26 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private inner class ClassScope(val clazz:IrClass) : InnerScopeImpl() {
|
||||
val isExported
|
||||
get() = clazz.descriptor.isExported()
|
||||
var offsetInBits = 0L
|
||||
val members = mutableListOf<DIDerivedTypeRef>()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val scope = if (isExported && context.shouldContainDebugInfo())
|
||||
DICreateReplaceableCompositeType(
|
||||
tag = DwarfTag.DW_TAG_structure_type.value,
|
||||
refBuilder = context.debugInfo.builder,
|
||||
refScope = context.debugInfo.compilationModule as DIScopeOpaqueRef,
|
||||
name = clazz.descriptor.typeInfoSymbolName,
|
||||
refFile = file().file(),
|
||||
line = clazz.line()) as DITypeOpaqueRef
|
||||
else null
|
||||
override fun classScope(): CodeContext? = this
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateReturnableBlock(value: IrReturnableBlockImpl): LLVMValueRef {
|
||||
context.log{"evaluateReturnableBlock : ${value.statements.forEach { ir2string(it) }}"}
|
||||
|
||||
@@ -1552,6 +1585,55 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
return result
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun debugClassDeclaration(declaration: IrClass, body: () -> Unit): Unit {
|
||||
val doDebugInfo = context.shouldContainDebugInfo() && declaration.descriptor.isExported()
|
||||
val classScope = currentCodeContext.classScope() as ClassScope
|
||||
if (doDebugInfo) context.debugInfo.types[declaration.descriptor.defaultType] = classScope.scope!!
|
||||
body()
|
||||
memScoped {
|
||||
if (doDebugInfo) context.debugInfo.types[declaration.descriptor.defaultType] = DICreateStructType(
|
||||
refBuilder = context.debugInfo.builder,
|
||||
scope = context.debugInfo.compilationModule as DIScopeOpaqueRef,
|
||||
name = declaration.descriptor.typeInfoSymbolName,
|
||||
file = file().file(),
|
||||
lineNumber = declaration.line(),
|
||||
sizeInBits = 64 /* TODO */,
|
||||
alignInBits = 4 /* TODO */,
|
||||
derivedFrom = null,
|
||||
elements = classScope.members.toCValues(),
|
||||
elementsCount = classScope.members.size.toLong(),
|
||||
refPlace = context.debugInfo.types[declaration.descriptor.defaultType] as DICompositeTypeRef,
|
||||
flags = 0
|
||||
) as DITypeOpaqueRef
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private fun debugFieldDeclaration(expression: IrField) {
|
||||
val scope = currentCodeContext.classScope() as? ClassScope ?: return
|
||||
if (!scope.isExported || !context.shouldContainDebugInfo()) return
|
||||
val irFile = (currentCodeContext.fileScope() as FileScope).file
|
||||
val sizeInBits = expression.descriptor.type.size(context)
|
||||
scope.offsetInBits += sizeInBits
|
||||
val alignInBits = expression.descriptor.type.alignment(context)
|
||||
scope.offsetInBits = alignTo(scope.offsetInBits, alignInBits)
|
||||
scope.members.add(DICreateMemberType(
|
||||
refBuilder = context.debugInfo.builder,
|
||||
refScope = scope.scope as DIScopeOpaqueRef,
|
||||
name = expression.descriptor.symbolName,
|
||||
file = irFile.file(),
|
||||
lineNum = expression.line(),
|
||||
sizeInBits = sizeInBits,
|
||||
alignInBits = alignInBits,
|
||||
offsetInBits = scope.offsetInBits,
|
||||
flags = 0,
|
||||
type = expression.descriptor.type.diType(context, codegen.llvmTargetData)
|
||||
)!!)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private fun IrFile.file(): DIFileRef {
|
||||
return context.debugInfo.files.getOrPut(this) {
|
||||
@@ -1561,11 +1643,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun IrFunction.scope():DIScopeOpaqueRef? {
|
||||
if (!context.shouldContainDebugInfo()) return null
|
||||
return context.debugInfo.subprograms.getOrPut(descriptor) {
|
||||
memScoped {
|
||||
val subroutineType = descriptor.subroutineType
|
||||
val subroutineType = descriptor.subroutineType(context, codegen.llvmTargetData)
|
||||
val functionLlvmValue = codegen.functionLlvmValue(descriptor)
|
||||
val linkageName = LLVMGetValueName(functionLlvmValue)!!.toKString()
|
||||
val diFunction = DICreateFunction(
|
||||
@@ -1586,37 +1669,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
} as DIScopeOpaqueRef
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private val FunctionDescriptor.subroutineType: DISubroutineTypeRef
|
||||
get() = memScoped {
|
||||
DICreateSubroutineType(context.debugInfo.builder, allocArrayOf(
|
||||
this@subroutineType.valueParameters.map{it.diType}),
|
||||
this@subroutineType.valueParameters.size)!!
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private val VariableDescriptor.diType: DITypeOpaqueRef
|
||||
get() = context.debugInfo.types.getOrPut(this.type) {
|
||||
when {
|
||||
KotlinBuiltIns.isInt(this.type) -> debugInfoBaseType("Int", LLVMInt32Type()!!)
|
||||
KotlinBuiltIns.isBoolean(this.type) -> debugInfoBaseType("Boolean", LLVMInt1Type()!!)
|
||||
KotlinBuiltIns.isChar(this.type) -> debugInfoBaseType("Char", LLVMInt8Type()!!)
|
||||
KotlinBuiltIns.isShort(this.type) -> debugInfoBaseType("Short", LLVMInt16Type()!!)
|
||||
KotlinBuiltIns.isByte(this.type) -> debugInfoBaseType("Byte", LLVMInt8Type()!!)
|
||||
KotlinBuiltIns.isLong(this.type) -> debugInfoBaseType("Long", LLVMInt64Type()!!)
|
||||
KotlinBuiltIns.isFloat(this.type) -> debugInfoBaseType("Float", LLVMFloatType()!!)
|
||||
KotlinBuiltIns.isDouble(this.type) -> debugInfoBaseType("Double", LLVMDoubleType()!!)
|
||||
(!KotlinBuiltIns.isPrimitiveType(this.type)) -> debugInfoBaseType("Any?", codegen.kObjHeaderPtr)
|
||||
else -> TODO(this.type.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun debugInfoBaseType(typeName:String, type:LLVMTypeRef) = DICreateBasicType(
|
||||
context.debugInfo.builder, typeName,
|
||||
LLVMSizeOfTypeInBits(codegen.llvmTargetData, type),
|
||||
LLVMPreferredAlignmentOfType(codegen.llvmTargetData, type).toLong(), 0) as DITypeOpaqueRef
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private val coroutineImplDescriptor = context.builtIns.getKonanInternalClass("CoroutineImpl")
|
||||
|
||||
Reference in New Issue
Block a user