From 9d63662fbe3637f3836c3ff235f49ee248d420e9 Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Fri, 21 Apr 2017 12:13:16 +0300 Subject: [PATCH] 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 --- .../jetbrains/kotlin/backend/konan/Context.kt | 16 +-- .../kotlin/backend/konan/llvm/DebugUtils.kt | 109 ++++++++++++++- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 128 ++++++++++++------ 3 files changed, 195 insertions(+), 58 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 77e7697dc88..fad4262b430 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -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() - val subprograms = mutableMapOf() - var builder: DIBuilderRef? = null - var module: DIModuleRef? = null - var compilationModule: DICompileUnitRef? = null - var types = mutableMapOf() - } + lateinit var debugInfo:DebugInfo } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt index 2fe4d799297..0c7714a0f8e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt @@ -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() + val subprograms = mutableMapOf() + var builder: DIBuilderRef? = null + var module: DIModuleRef? = null + var compilationModule: DICompileUnitRef? = null + var types = mutableMapOf() + val llvmTypes = mapOf( + 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(context.builtIns.byteType, context.builtIns.shortType, context.builtIns.longType) + val realTypes = listOf(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 + 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 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 +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 39d51b96720..823b035a7c0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -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() + @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")