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:
committed by
vvlevchenko
parent
747ad0efda
commit
1544045643
+15
-2
@@ -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>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+24
-4
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+84
@@ -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)
|
||||
}
|
||||
}
|
||||
+249
-74
@@ -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)
|
||||
|
||||
|
||||
+3
@@ -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)
|
||||
|
||||
+16
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user