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:
Vasily Levchenko
2017-04-21 12:13:16 +03:00
committed by vvlevchenko
parent b1462f8da6
commit 9d63662fbe
3 changed files with 195 additions and 58 deletions
@@ -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
}
@@ -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
}
@@ -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")