From 1544045643e5db1f8c03d3e8f7e24cad2ca9a6b5 Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Mon, 20 Mar 2017 18:48:02 +0300 Subject: [PATCH] CODEGEN: Initial support for emiting debug information. - avoiding generating debug info using function scope (prolog/epilog problem). - bit logging. - extracted function for debug location. - attempted to reset debug location at the start of the function (not working but looks like it's right way). - private modifier added to non public functions - removied IrFunction::line copy of IrElement::line() extention function. - added FileScope used in IrToBitcode::visitFile() - local variable support - mechanism of central managment of debug info emitting --- .../jetbrains/kotlin/backend/konan/Context.kt | 17 +- .../backend/konan/KonanConfigurationKeys.kt | 2 +- .../backend/konan/llvm/CodeGenerator.kt | 28 +- .../kotlin/backend/konan/llvm/DebugUtils.kt | 84 +++++ .../kotlin/backend/konan/llvm/IrToBitcode.kt | 323 ++++++++++++++---- .../kotlin/backend/konan/llvm/LlvmUtils.kt | 3 + .../backend/konan/llvm/VariableManager.kt | 16 + 7 files changed, 392 insertions(+), 81 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt 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 caeb4ea311f..d1331427a53 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 @@ -16,8 +16,7 @@ package org.jetbrains.kotlin.backend.konan -import llvm.LLVMDumpModule -import llvm.LLVMModuleRef +import llvm.* import org.jetbrains.kotlin.backend.common.validateIrModule import org.jetbrains.kotlin.backend.jvm.descriptors.initialize import org.jetbrains.kotlin.backend.konan.descriptors.* @@ -407,10 +406,24 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { return config.configuration.getBoolean(KonanConfigKeys.TIME_PHASES) } + fun shouldContainDebugInfo(): Boolean { + return config.configuration.getBoolean(KonanConfigKeys.DEBUG) + } + fun log(message: String) { if (phase?.verbose ?: false) { println(message) } } + + 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() + } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 24714f33164..f40c099a91d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -38,7 +38,7 @@ class KonanConfigKeys { val OPTIMIZATION: CompilerConfigurationKey = CompilerConfigurationKey.create("optimized compilation") val DEBUG: CompilerConfigurationKey - = CompilerConfigurationKey.create("compilation with debug information") + = CompilerConfigurationKey.create("add debug information") val NOSTDLIB: CompilerConfigurationKey = CompilerConfigurationKey.create("don't link with stdlib") val NOLINK: CompilerConfigurationKey diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 788d68445fe..965621d02b7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -43,11 +43,11 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { val intPtrType = LLVMIntPtrType(llvmTargetData)!! private val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!! - fun prologue(descriptor: FunctionDescriptor) { + fun prologue(descriptor: FunctionDescriptor, locationInfo: LocationInfo? = null) { val llvmFunction = llvmFunction(descriptor) prologue(llvmFunction, - LLVMGetReturnType(getLlvmFunctionType(descriptor))!!) + LLVMGetReturnType(getLlvmFunctionType(descriptor))!!, locationInfo) if (!descriptor.isExported()) { LLVMSetLinkage(llvmFunction, LLVMLinkage.LLVMInternalLinkage) @@ -60,7 +60,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { functionDescriptor = descriptor } - fun prologue(function:LLVMValueRef, returnType:LLVMTypeRef) { + fun prologue(function:LLVMValueRef, returnType:LLVMTypeRef, locationInfo: LocationInfo? = null) { assert(returns.size == 0) assert(this.function != function) @@ -75,6 +75,9 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { epilogueBb = LLVMAppendBasicBlock(function, "epilogue") cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!! positionAtEnd(entryBb!!) + locationInfo?.let { + debugLocation(it) + } slotsPhi = phi(kObjHeaderPtrPtr) // First slot can be assigned to keep pointer to frame local arena. slotCount = 1 @@ -84,7 +87,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { or(ptrToInt(slotsPhi, intPtrType), immOneIntPtrType), kObjHeaderPtrPtr) } - fun epilogue() { + fun epilogue(locationInfo: LocationInfo? = null) { appendingTo(prologueBb!!) { val slots = if (needSlots) LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!! @@ -95,6 +98,9 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { val slotsMem = bitcast(kInt8Ptr, slots) val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt() val alignment = LLVMABIAlignmentOfType(llvmTargetData, kObjHeaderPtr) + locationInfo?.let { + debugLocation(it) + } call(context.llvm.memsetFunction, listOf(slotsMem, Int8(0).llvm, Int32(slotCount * pointerSize).llvm, Int32(alignment).llvm, @@ -488,6 +494,20 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { fun llvmFunction(function: FunctionDescriptor): LLVMValueRef = function.llvmFunction + internal fun resetDebugLocation() { + if (!context.shouldContainDebugInfo()) return + LLVMBuilderResetDebugLocation(builder) + } + + internal fun debugLocation(locationInfo: LocationInfo):DILocationRef? { + if (!context.shouldContainDebugInfo()) return null + return LLVMBuilderSetDebugLocation( + builder, + locationInfo.line, + locationInfo.column, + locationInfo.scope) + } + } 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 new file mode 100644 index 00000000000..e224d704e18 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.konan.llvm + +import llvm.DICreateCompilationUnit +import llvm.DICreateModule +import llvm.DIScopeOpaqueRef +import llvm.LLVMAddNamedMetadataOperand +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanConfigKeys +import org.jetbrains.kotlin.backend.konan.KonanVersion + + +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 + val dwarfVersionMetaDataNodeName = "Dwarf Name".mdString() + val dwarfDebugInfoMetaDataNodeName = "Debug Info Version".mdString() + val dwarfVersion = 2 /* TODO: configurable? like gcc/clang -gdwarf-2 and so on. */ + val debugInfoVersion = 3 /* TODO: configurable? */ +} + +internal fun generateDebugInfoHeader(context: Context) { + if (context.shouldContainDebugInfo()) { + context.debugInfo.module = DICreateModule( + builder = context.debugInfo.builder, + scope = context.llvmModule as DIScopeOpaqueRef, + name = context.config.configuration.get(KonanConfigKeys.BITCODE_FILE)!!, + configurationMacro = "", + includePath = "", + iSysRoot = "") + context.debugInfo.compilationModule = DICreateCompilationUnit( + builder = context.debugInfo.builder, + lang = DWARF.DW_LANG_kotlin, + File = context.config.configuration.get(KonanConfigKeys.BITCODE_FILE)!!, + dir = "", + producer = DWARF.producer, + isOptimized = 0, + flags = "", + rv = DWARF.runtimeVersion) + /* TODO: figure out what here 2 means: + * + * 0:b-backend-dwarf:minamoto@minamoto-osx(0)# cat /dev/null | clang -xc -S -emit-llvm -g -o - - + * ; ModuleID = '-' + * source_filename = "-" + * target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + * target triple = "x86_64-apple-macosx10.12.0" + * + * !llvm.dbg.cu = !{!0} + * !llvm.module.flags = !{!3, !4, !5} + * !llvm.ident = !{!6} + * + * !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "Apple LLVM version 8.0.0 (clang-800.0.38)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + * !1 = !DIFile(filename: "-", directory: "/Users/minamoto/ws/.git-trees/backend-dwarf") + * !2 = !{} + * !3 = !{i32 2, !"Dwarf Version", i32 2} ; <- + * !4 = !{i32 2, !"Debug Info Version", i32 700000003} ; <- + * !5 = !{i32 1, !"PIC Level", i32 2} + * !6 = !{!"Apple LLVM version 8.0.0 (clang-800.0.38)"} + */ + val llvmTwo = Int32(2).llvm + val dwarfVersion = node(llvmTwo, DWARF.dwarfVersionMetaDataNodeName, Int32(DWARF.dwarfVersion).llvm) + val nodeDebugInfoVersion = node(llvmTwo, DWARF.dwarfDebugInfoMetaDataNodeName, Int32(DWARF.debugInfoVersion).llvm) + val llvmModuleFlags = "llvm.module.flags" + LLVMAddNamedMetadataOperand(context.llvmModule, llvmModuleFlags, dwarfVersion) + LLVMAddNamedMetadataOperand(context.llvmModule, llvmModuleFlags, nodeDebugInfoVersion) + } +} 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 2f6b2225877..7cb421b9270 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 @@ -58,6 +58,7 @@ internal fun emitLLVM(context: Context) { // used to compile runtime.bc. val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose context.llvmModule = llvmModule + context.debugInfo.builder = DICreateBuilder(llvmModule) context.llvmDeclarations = createLlvmDeclarations(context) @@ -67,6 +68,9 @@ internal fun emitLLVM(context: Context) { irModule.acceptVoid(RTTIGeneratorVisitor(context)) } + + generateDebugInfoHeader(context) + phaser.phase(KonanPhase.CODEGEN) { irModule.acceptVoid(CodeGeneratorVisitor(context)) } @@ -85,10 +89,13 @@ internal fun emitLLVM(context: Context) { } } - val outFile = context.config.configuration.get(KonanConfigKeys.BITCODE_FILE)!! - LLVMWriteBitcodeToFile(llvmModule, outFile) + if (context.shouldContainDebugInfo()) { + DIFinalize(context.debugInfo.builder) + } + LLVMWriteBitcodeToFile(llvmModule, context.config.configuration.get(KonanConfigKeys.BITCODE_FILE)!!) } + internal fun verifyModule(llvmModule: LLVMModuleRef, current: String = "") { memScoped { val errorRef = allocPointerTo() @@ -182,8 +189,14 @@ internal interface CodeContext { * * @return the requested value */ - fun functionScope(): CodeContext + fun functionScope(): CodeContext? + /** + * Returns owning file scope. + * + * @return the requested value + */ + fun fileScope(): CodeContext? } //-------------------------------------------------------------------------// @@ -223,7 +236,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid override fun genGetValue(descriptor: ValueDescriptor) = unsupported(descriptor) - override fun functionScope(): CodeContext = unsupported() + override fun functionScope(): CodeContext? = null + + override fun fileScope(): CodeContext? = null } /** @@ -314,25 +329,26 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } //-------------------------------------------------------------------------// - override fun visitFile(declaration: IrFile) { context.llvm.fileInitializers.clear() - declaration.acceptChildrenVoid(this) + using(FileScope(declaration)) { + declaration.acceptChildrenVoid(this) - if (context.llvm.fileInitializers.isEmpty()) - return + if (context.llvm.fileInitializers.isEmpty()) + return - // Create global initialization records. - val fileName = declaration.name.takeLastWhile { it != '/' }.dropLastWhile { it != '.' }.dropLast(1) - val initName = "${fileName}_init_${context.llvm.globalInitIndex}" - val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}" - val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}" + // Create global initialization records. + val fileName = declaration.name.takeLastWhile { it != '/' }.dropLastWhile { it != '.' }.dropLast(1) + val initName = "${fileName}_init_${context.llvm.globalInitIndex}" + val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}" + val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}" - val initFunction = createInitBody(initName) - val initNode = createInitNode(initFunction, nodeName) - createInitCtor(ctorName, initNode) + val initFunction = createInitBody(initName) + val initNode = createInitNode(initFunction, nodeName) + createInitCtor(ctorName, initNode) + } } //-------------------------------------------------------------------------// @@ -513,29 +529,32 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid if (declaration.descriptor.isExternal) return if (body == null) return - codegen.prologue(declaration.descriptor) - using(FunctionScope(declaration)) { + val locationInfo = LocationInfo( + scope = declaration.scope(), + line = declaration.line(), + column = declaration.column()) + codegen.prologue(declaration.descriptor, locationInfo) + using(VariableScope()) { - context.log("generateBlockBody : ${ir2string(body)}") when (body) { - is IrBlockBody -> body.statements.forEach { generateStatement(it) } + is IrBlockBody -> body.statements.forEach { generateStatement(it) } is IrExpressionBody -> generateStatement(body.expression) - is IrSyntheticBody -> throw AssertionError("Synthetic body ${body.kind} has not been lowered") + is IrSyntheticBody -> throw AssertionError("Synthetic body ${body.kind} has not been lowered") else -> TODO(ir2string(body)) } } - } + codegen.resetDebugLocation() + if (!codegen.isAfterTerminator()) { + if (codegen.returnType == voidType) + codegen.ret(null) + else + codegen.unreachable() + } - if (!codegen.isAfterTerminator()) { - if (codegen.returnType == voidType) - codegen.ret(null) - else - codegen.unreachable() + codegen.epilogue(locationInfo) } - codegen.epilogue() - if (context.shouldVerifyBitCode()) verifyModule(context.llvmModule!!, "${declaration.descriptor.containingDeclaration}::${ir2string(declaration)}") @@ -1054,6 +1073,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid expression.branches.forEach { // Iterate through "when" branches (clauses). var bbNext = bbExit // For last clause bbNext coincides with bbExit. + debugLocation(it) if (it != expression.branches.last()) // If it is not last clause. bbNext = codegen.basicBlock("when_next") // Create new basic block for next clause. generateWhenCase(resultPhi, it, bbNext, bbExit) // Generate code for current clause. @@ -1072,19 +1092,21 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateWhileLoop(loop: IrWhileLoop): LLVMValueRef { val loopScope = LoopScope(loop) - using(loopScope) { - val loopBody = codegen.basicBlock("while_loop") - codegen.br(loopScope.loopCheck) + debugInfo(loop) { + using(loopScope) { + val loopBody = codegen.basicBlock("while_loop") + codegen.br(loopScope.loopCheck) - codegen.positionAtEnd(loopScope.loopCheck) - val condition = evaluateExpression(loop.condition) - codegen.condBr(condition, loopBody, loopScope.loopExit) + codegen.positionAtEnd(loopScope.loopCheck) + val condition = evaluateExpression(loop.condition) + codegen.condBr(condition, loopBody, loopScope.loopExit) - codegen.positionAtEnd(loopBody) - loop.body?.generate() + codegen.positionAtEnd(loopBody) + loop.body?.generate() - codegen.br(loopScope.loopCheck) - codegen.positionAtEnd(loopScope.loopExit) + codegen.br(loopScope.loopCheck) + codegen.positionAtEnd(loopScope.loopExit) + } } @@ -1119,7 +1141,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateGetValue(value: IrGetValue): LLVMValueRef { context.log("evaluateGetValue : ${ir2string(value)}") - return currentCodeContext.genGetValue(value.descriptor) + return debugInfo(value) { + currentCodeContext.genGetValue(value.descriptor) + } } //-------------------------------------------------------------------------// @@ -1128,7 +1152,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid context.log("evaluateSetVariable : ${ir2string(value)}") val result = evaluateExpression(value.value) val variable = currentCodeContext.getDeclaredVariable(value.descriptor) - codegen.vars.store(result, variable) + debugInfo(value) { + codegen.vars.store(result, variable) + } assert(value.type.isUnit()) return codegen.theUnitInstanceRef.llvm @@ -1139,24 +1165,42 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun generateVariable(value: IrVariable) { context.log("generateVariable : ${ir2string(value)}") val result = value.initializer?.let { evaluateExpression(it) } - currentCodeContext.genDeclareVariable(value.descriptor, result) + val variableDescriptor = value.descriptor + val index = currentCodeContext.genDeclareVariable(variableDescriptor, result) + if (context.shouldContainDebugInfo()) { + val location = debugLocation(value) + val functionScope = (currentCodeContext.functionScope() as FunctionScope).declaration?.scope() ?: return + val file = (currentCodeContext.fileScope() as FileScope).file.file() + val variable = codegen.vars.load(index) + val line = value.line() + codegen.vars.debugInfoLocalVariableLocation( + functionScope = functionScope, + diType = variableDescriptor.diType, + name = variableDescriptor.name, + variable = variable, + file = file, + line = line, + location = location) + } } //-------------------------------------------------------------------------// private fun evaluateTypeOperator(value: IrTypeOperatorCall): LLVMValueRef { - when (value.operator) { - IrTypeOperator.CAST -> return evaluateCast(value) - IrTypeOperator.IMPLICIT_INTEGER_COERCION -> return evaluateIntegerCoercion(value) - IrTypeOperator.IMPLICIT_CAST -> return evaluateExpression(value.argument) - IrTypeOperator.IMPLICIT_NOTNULL -> TODO("${ir2string(value)}") - IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { - evaluateExpression(value.argument) - return codegen.theUnitInstanceRef.llvm + return debugInfo(value) { + when (value.operator) { + IrTypeOperator.CAST -> evaluateCast(value) + IrTypeOperator.IMPLICIT_INTEGER_COERCION -> evaluateIntegerCoercion(value) + IrTypeOperator.IMPLICIT_CAST -> evaluateExpression(value.argument) + IrTypeOperator.IMPLICIT_NOTNULL -> TODO(ir2string(value)) + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { + evaluateExpression(value.argument) + return@debugInfo codegen.theUnitInstanceRef.llvm + } + IrTypeOperator.SAFE_CAST -> throw IllegalStateException("safe cast wasn't lowered") + IrTypeOperator.INSTANCEOF -> evaluateInstanceOf(value) + IrTypeOperator.NOT_INSTANCEOF -> evaluateNotInstanceOf(value) } - IrTypeOperator.SAFE_CAST -> throw IllegalStateException("safe cast wasn't lowered") - IrTypeOperator.INSTANCEOF -> return evaluateInstanceOf(value) - IrTypeOperator.NOT_INSTANCEOF -> return evaluateNotInstanceOf(value) } } @@ -1174,14 +1218,16 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val type = value.typeOperand assert(type.isPrimitiveInteger()) val result = evaluateExpression(value.argument) - val llvmSrcType = codegen.getLLVMType(value.argument.type) - val llvmDstType = codegen.getLLVMType(type) - val srcWidth = LLVMGetIntTypeWidth(llvmSrcType) - val dstWidth = LLVMGetIntTypeWidth(llvmDstType) - return when { - srcWidth == dstWidth -> result - srcWidth > dstWidth -> LLVMBuildTrunc(codegen.builder, result, llvmDstType, "")!! - else /* srcWidth < dstWidth */ -> LLVMBuildSExt(codegen.builder, result, llvmDstType, "")!! + return debugInfo(value) { + val llvmSrcType = codegen.getLLVMType(value.argument.type) + val llvmDstType = codegen.getLLVMType(type) + val srcWidth = LLVMGetIntTypeWidth(llvmSrcType) + val dstWidth = LLVMGetIntTypeWidth(llvmDstType) + return@debugInfo when { + srcWidth == dstWidth -> result + srcWidth > dstWidth -> LLVMBuildTrunc(codegen.builder, result, llvmDstType, "")!! + else /* srcWidth < dstWidth */ -> LLVMBuildSExt(codegen.builder, result, llvmDstType, "")!! + } } } @@ -1270,13 +1316,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid context.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null) { val thisPtr = evaluateExpression(value.receiver!!) - return codegen.loadSlot( - fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) + return debugInfo(value) { + codegen.loadSlot( + fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) + } } else { assert (value.receiver == null) val ptr = context.llvmDeclarations.forStaticField(value.descriptor).storage - return codegen.loadSlot(ptr, value.descriptor.isVar()) + return debugInfo(value) { + codegen.loadSlot(ptr, value.descriptor.isVar()) + } } } @@ -1301,12 +1351,16 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid if (value.descriptor.dispatchReceiverParameter != null) { val thisPtr = evaluateExpression(value.receiver!!) - codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)) + debugInfo(value) { + codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)) + } } else { assert (value.receiver == null) val globalValue = context.llvmDeclarations.forStaticField(value.descriptor).storage - codegen.storeAnyGlobal(valueToAssign, globalValue) + debugInfo(value) { + codegen.storeAnyGlobal(valueToAssign, globalValue) + } } assert (value.type.isUnit()) @@ -1361,6 +1415,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateConst(value: IrConst<*>): LLVMValueRef { context.log("evaluateConst : ${ir2string(value)}") + debugLocation(value) when (value.kind) { IrConstKind.Null -> return codegen.kNullObjHeaderPtr IrConstKind.Boolean -> when (value.value) { @@ -1395,6 +1450,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid evaluated } + debugLocation(expression) currentCodeContext.genReturn(target, ret) return codegen.kNothingFakeValue } @@ -1437,6 +1493,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// + private inner class FileScope(val file:IrFile) : InnerScopeImpl() { + override fun fileScope(): CodeContext? = this + } + + //-------------------------------------------------------------------------// + private fun evaluateInlineFunction(value: IrInlineFunctionBody): LLVMValueRef { context.log("evaluateInlineFunction : ${value.statements.forEach { ir2string(it) }}") @@ -1540,6 +1602,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid return null } + //-------------------------------------------------------------------------// private fun evaluateCall(value: IrMemberAccessExpression): LLVMValueRef { context.log("evaluateCall : ${ir2string(value)}") @@ -1547,18 +1610,128 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid compileTimeEvaluate(value, args)?.let { return it } - when { - value is IrDelegatingConstructorCall -> - return delegatingConstructorCall(value.descriptor, args) + debugInfo(value) { + when { + value is IrDelegatingConstructorCall -> + return delegatingConstructorCall(value.descriptor, args) - value.descriptor is FunctionDescriptor -> return evaluateFunctionCall( - value as IrCall, args, resultLifetime(value)) - else -> { - TODO("${ir2string(value)}") + value.descriptor is FunctionDescriptor -> + return evaluateFunctionCall( + value as IrCall, args, resultLifetime(value)) + else -> { + TODO(ir2string(value)) + } } } + TODO(ir2string(value)) + } + + //-------------------------------------------------------------------------// + private fun file() = (currentCodeContext.fileScope() as FileScope).file + + //-------------------------------------------------------------------------// + private fun debugLocation(element: IrElement):DILocationRef? { + if (!context.shouldContainDebugInfo()) return null + val functionScope = currentCodeContext.functionScope() as? FunctionScope ?: return null + val scope = functionScope.declaration ?: return null + val diScope = scope.scope() + @Suppress("UNCHECKED_CAST") + return codegen.debugLocation(LocationInfo( + line = element.line(), + column = element.column(), + scope = diScope as DIScopeOpaqueRef)) + } + + //-------------------------------------------------------------------------// + private fun IrElement.line(): Int { + return if (this.startOffset < 0) + -1 + else + file().fileEntry.getLineNumber(this.startOffset) + } + + //-------------------------------------------------------------------------// + private fun IrElement.column(): Int { + return if (this.startOffset < 0) + -1 + else + file().fileEntry.getColumnNumber(this.startOffset) + } + + //-------------------------------------------------------------------------// + private inline fun debugInfo(element: IrElement, body:() -> T): T { + debugLocation(element) + val result = body() + codegen.resetDebugLocation() + return result + } + + //-------------------------------------------------------------------------// + private fun IrFile.file(): DIFileRef { + return context.debugInfo.files.getOrPut(this) { + val path = this.fileEntry.name.split("/") + DICreateFile(context.debugInfo.builder, path.last(), path.dropLast(1).joinToString("/"))!! + } } + //-------------------------------------------------------------------------// + private fun IrFunction.scope():DIScopeOpaqueRef? { + if (!context.shouldContainDebugInfo()) return null + return context.debugInfo.subprograms.getOrPut(descriptor) { + memScoped { + val subroutineType = descriptor.subroutineType + val functionLlvmValue = codegen.functionLlvmValue(descriptor) + val linkageName = LLVMGetValueName(functionLlvmValue)!!.toKString() + val diFunction = DICreateFunction( + builder = context.debugInfo.builder, + scope = context.debugInfo.compilationModule as DIScopeOpaqueRef, + name = descriptor.name.asString(), + linkageName = linkageName, + file = file().file(), + lineNo = line(), + type = subroutineType, + //TODO: need more investigations. + isLocal = 0, + isDefinition = 1, + scopeLine = 0) + DIFunctionAddSubprogram(functionLlvmValue , diFunction) + diFunction!! + } + } 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 + + + //-------------------------------------------------------------------------// /** * Evaluates all arguments of [expression] that are explicitly represented in the IR. * Returns results in the same order as LLVM function expects, assuming that all explicit arguments @@ -1896,7 +2069,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid memScoped { val arrayLength = args.size val argsCasted = args.map { it -> constPointer(it).bitcast(int8TypePtr) } - val llvmUsedGlobal = + val llvmUsedGlobal = context.llvm.staticData.placeGlobalArray("llvm.used", int8TypePtr, argsCasted) LLVMSetLinkage(llvmUsedGlobal.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage); @@ -1953,3 +2126,5 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } } +internal data class LocationInfo(val scope:DIScopeOpaqueRef?, val line:Int, val column:Int) + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index c5672fb7b88..8090b26c95e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -256,3 +256,6 @@ fun parseBitcodeFile(path: String): LLVMModuleRef = memScoped { LLVMDisposeMemoryBuffer(memoryBuffer) } } + +internal fun String.mdString() = LLVMMDString(this, this.length)!! +internal fun node(vararg it:LLVMValueRef) = LLVMMDNode(it.toList().toCValues(), it.size) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt index ed482911a36..4af209e87ca 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt @@ -126,4 +126,20 @@ internal class VariableManager(val codegen: CodeGenerator) { fun store(value: LLVMValueRef, index: Int) { variables[index].store(value) } + + fun debugInfoLocalVariableLocation(functionScope: DIScopeOpaqueRef, diType: DITypeOpaqueRef, name:Name, variable: LLVMValueRef, file: DIFileRef, line: Int, location: DILocationRef?) { + val variableDeclaration = DICreateAutoVariable( + builder = codegen.context.debugInfo.builder, + scope = functionScope, + name = name.asString(), + file = file, + line = line, + type = diType) + DIInsertDeclarationWithEmptyExpression( + builder = codegen.context.debugInfo.builder, + value = variable, + localVariable = variableDeclaration, + location = location, + bb = LLVMGetInsertBlock(codegen.builder)) + } }