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
This commit is contained in:
Vasily Levchenko
2017-03-20 18:48:02 +03:00
committed by vvlevchenko
parent 747ad0efda
commit 1544045643
7 changed files with 392 additions and 81 deletions
@@ -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<IrFile, DIFileRef>()
val subprograms = mutableMapOf<FunctionDescriptor, DISubprogramRef>()
var builder: DIBuilderRef? = null
var module: DIModuleRef? = null
var compilationModule: DICompileUnitRef? = null
var types = mutableMapOf<KotlinType, DITypeOpaqueRef>()
}
}
@@ -38,7 +38,7 @@ class KonanConfigKeys {
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("optimized compilation")
val DEBUG: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("compilation with debug information")
= CompilerConfigurationKey.create("add debug information")
val NOSTDLIB: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with stdlib")
val NOLINK: CompilerConfigurationKey<Boolean>
@@ -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)
}
}
@@ -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)
}
}
@@ -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<ByteVar>()
@@ -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 <T> 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)
@@ -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)
@@ -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))
}
}