[codegen][debug] generation debug info for vals.

This commit is contained in:
Vasily Levchenko
2017-10-25 18:26:09 +03:00
committed by Vasily Levchenko
parent b6713df52a
commit fea734ec83
8 changed files with 163 additions and 62 deletions
@@ -365,6 +365,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
}
}
lateinit var debugInfo:DebugInfo
lateinit var debugInfo: DebugInfo
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
internal class CodeGenerator(override val context: Context) : ContextUtils {
@@ -478,6 +479,12 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
private fun position() = basicBlockToLastLocation[currentBlock]
internal fun mapParameterForDebug(index: Int, value: LLVMValueRef) {
appendingTo(localsInitBb) {
LLVMBuildStore(builder, value, vars.addressOf(index))
}
}
internal fun prologue() {
assert(returns.isEmpty())
if (isObjectType(returnType!!)) {
@@ -505,7 +512,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
Int32(slotCount * codegen.runtime.pointerSize).llvm,
Int32(codegen.runtime.pointerAlignment).llvm,
Int1(0).llvm))
call(context.llvm.enterFrameFunction, listOf(slots, Int32(slotCount).llvm))
call(context.llvm.enterFrameFunction, listOf(slots, Int32(vars.skip).llvm, Int32(slotCount).llvm))
}
addPhiIncoming(slotsPhi!!, prologueBb to slots)
memScoped {
@@ -669,7 +676,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
private fun releaseVars() {
if (needSlots) {
call(context.llvm.leaveFrameFunction,
listOf(slotsPhi!!, Int32(slotCount).llvm))
listOf(slotsPhi!!, Int32(vars.skip).llvm, Int32(slotCount).llvm))
}
}
}
@@ -31,8 +31,10 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnableBlockImpl
import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -463,17 +465,30 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
/**
* The scope of parameter visibility.
*/
private open inner class ParameterScope(private val functionGenerationContext: FunctionGenerationContext,
parameters: Map<ParameterDescriptor, LLVMValueRef>): InnerScopeImpl() {
private open inner class ParameterScope(
function: IrFunction?,
private val functionGenerationContext: FunctionGenerationContext): InnerScopeImpl() {
// Note: it is possible to generate access to a parameter without any variables,
// however variables are named and thus the resulting bitcode can be more readable.
val parameters = bindParameters(function?.descriptor)
init {
parameters.map { (descriptor, value) ->
functionGenerationContext.vars.createImmutable(descriptor, value)
}
if (function != null) {
parameters.forEach{
val descriptor = it.key
val ir = when {
descriptor is ValueParameterDescriptor -> function.getIrValueParameter(descriptor)
descriptor is ReceiverParameterDescriptor && function.extensionReceiverParameter?.descriptor == descriptor -> function.extensionReceiverParameter!!
descriptor is ReceiverParameterDescriptor && function.dispatchReceiverParameter?.descriptor == descriptor-> function.dispatchReceiverParameter!!
function.descriptor is ClassConstructorDescriptor && (function.descriptor as ClassConstructorDescriptor).constructedClass.thisAsReceiverParameter == descriptor-> IrValueParameterImpl(function.startOffset, function.startOffset, object: IrDeclarationOriginImpl("THIS"){}, descriptor)
else -> TODO()
}
val local = functionGenerationContext.vars.createParameter(descriptor,
it.value, debugInfoIfNeeded(function, ir))
functionGenerationContext.mapParameterForDebug(local, it.value)
}
}
}
override fun genGetValue(descriptor: ValueDescriptor): LLVMValueRef {
@@ -489,10 +504,12 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
/**
* The [CodeContext] enclosing the entire function body.
*/
private inner class FunctionScope (val declaration: IrFunction?, val functionGenerationContext: FunctionGenerationContext) :
ParameterScope(functionGenerationContext, bindParameters(declaration?.descriptor)) {
private inner class FunctionScope (val declaration: IrFunction?, val functionGenerationContext: FunctionGenerationContext) : InnerScopeImpl() {
constructor(llvmFunction:LLVMValueRef, functionGenerationContext: FunctionGenerationContext):this(null, functionGenerationContext) {
this.llvmFunction = llvmFunction
}
var llvmFunction:LLVMValueRef? = declaration?.let{
@@ -531,7 +548,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
/**
* Binds LLVM function parameters to IR parameter descriptors.
*/
private fun bindParameters(descriptor: FunctionDescriptor?): Map<ParameterDescriptor, LLVMValueRef> {
private fun bindParameters(descriptor: FunctionDescriptor?): Map<ParameterDescriptor, LLVMValueRef> {
if (descriptor == null) return emptyMap()
val parameterDescriptors = descriptor.allParameters
return parameterDescriptors.mapIndexed { i, parameterDescriptor ->
@@ -549,22 +566,27 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
if (declaration.descriptor.isExternal) return
if (body == null) return
val scope = declaration.scope()
val startLine = declaration.startLine()
val startLocationInfo = LocationInfo(
scope = declaration.scope(),
line = declaration.startLine(),
scope = scope,
line = startLine,
column = declaration.startColumn())
val endLocationInfo = LocationInfo(
scope = declaration.scope(),
scope = scope,
line = declaration.endLine(),
column = declaration.endColumn())
generateFunction(codegen, declaration.descriptor, startLocationInfo, endLocationInfo) {
using(FunctionScope(declaration, it)) {
using(VariableScope()) {
when (body) {
is IrBlockBody -> body.statements.forEach { generateStatement(it) }
is IrExpressionBody -> generateStatement(body.expression)
is IrSyntheticBody -> throw AssertionError("Synthetic body ${body.kind} has not been lowered")
else -> TODO(ir2string(body))
val parameterScope = ParameterScope(declaration, functionGenerationContext)
using(parameterScope) {
using(VariableScope()) {
when (body) {
is IrBlockBody -> body.statements.forEach { generateStatement(it) }
is IrExpressionBody -> generateStatement(body.expression)
is IrSyntheticBody -> throw AssertionError("Synthetic body ${body.kind} has not been lowered")
else -> TODO(ir2string(body))
}
}
}
}
@@ -1070,25 +1092,41 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
//-------------------------------------------------------------------------//
private fun generateVariable(value: IrVariable) {
context.log{"generateVariable : ${ir2string(value)}"}
val result = value.initializer?.let { evaluateExpression(it) }
val variableDescriptor = value.descriptor
val functionScope = (currentCodeContext.functionScope() as FunctionScope).declaration?.scope()
val variableLocation = if (context.shouldContainDebugInfo() && functionScope != null && variableDescriptor.isVar) {
val location = debugLocation(value)
val file = (currentCodeContext.fileScope() as FileScope).file.file()
val line = value.startLine()
functionGenerationContext.vars.debugInfoLocalVariableLocation(
private fun debugInfoIfNeeded(function: IrFunction?, element: IrElement): VariableDebugLocation? {
if (function == null || !context.shouldContainDebugInfo()) return null
val functionScope = function?.scope()
if (functionScope == null || !element.needDebugInfo(context)) return null
val location = debugLocation(element)
val file = (currentCodeContext.fileScope() as FileScope).file.file()
val line = element.startLine()
return when (element) {
is IrVariable -> debugInfoLocalVariableLocation(
builder = context.debugInfo.builder,
functionScope = functionScope,
diType = variableDescriptor.type.diType(context, functionGenerationContext.llvmTargetData),
name = variableDescriptor.name,
diType = element.descriptor.type.diType(context, codegen.llvmTargetData),
name = element.descriptor.name,
file = file,
line = line,
location = location)
} else null
currentCodeContext.genDeclareVariable(variableDescriptor, result, variableLocation)
is IrValueParameter -> debugInfoParameterLocation(
builder = context.debugInfo.builder,
functionScope = functionScope,
diType = element.descriptor.type.diType(context, codegen.llvmTargetData),
name = element.descriptor.name,
argNo = (element.descriptor as? ValueParameterDescriptor)?.index ?: 0,
file = file,
line = line,
location = location)
else -> throw Error("Unsupported element type: ${ir2string(element)}")
}
}
private fun generateVariable(variable: IrVariable) {
context.log{"generateVariable : ${ir2string(variable)}"}
val value = variable.initializer?.let { evaluateExpression(it) }
currentCodeContext.genDeclareVariable(
variable.descriptor, value, debugInfoIfNeeded(
(currentCodeContext.functionScope() as FunctionScope)?.declaration, variable))
}
//-------------------------------------------------------------------------//
@@ -2414,3 +2452,4 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
internal data class LocationInfo(val scope:DIScopeOpaqueRef?, val line:Int, val column:Int)
private fun IrFunction.hasNotReceiver() = this.extensionReceiverParameter == null && this.dispatchReceiverParameter == null
@@ -17,9 +17,13 @@
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.name.Name
internal fun IrElement.needDebugInfo(context: Context) = context.shouldContainDebugInfo() || (this is IrVariable && this.descriptor.isVar)
internal class VariableManager(val functionGenerationContext: FunctionGenerationContext) {
internal interface Record {
@@ -35,6 +39,13 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}"
}
inner class ParameterRecord(val address: LLVMValueRef, val refSlot: Boolean) : Record {
override fun load() : LLVMValueRef = functionGenerationContext.loadSlot(address, false)
override fun store(value: LLVMValueRef) = throw Error("writing to parameter")
override fun address() : LLVMValueRef = this.address
override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}"
}
class ValueRecord(val value: LLVMValueRef, val name: Name) : Record {
override fun load() : LLVMValueRef = value
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${name}")
@@ -51,19 +62,20 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
contextVariablesToIndex.clear()
}
fun createVariable(descriptor: VariableDescriptor, value: LLVMValueRef? = null, variableLocation: VariableDebugLocation?) : Int {
fun createVariable(descriptor: ValueDescriptor, value: LLVMValueRef? = null, variableLocation: VariableDebugLocation?) : Int {
val isVar = descriptor is VariableDescriptor && descriptor.isVar
// Note that we always create slot for object references for memory management.
if (!descriptor.isVar && value != null)
if (!functionGenerationContext.context.shouldContainDebugInfo() && !isVar && value != null)
return createImmutable(descriptor, value)
else
// Unfortunately, we have to create mutable slots here,
// as even vals can be assigned on multiple paths. However, we use varness
// knowledge, as anonymous slots are created only for true vars (for vals
// their single assigner already have slot).
return createMutable(descriptor, descriptor.isVar, value, variableLocation)
return createMutable(descriptor, isVar, value, variableLocation)
}
fun createMutable(descriptor: VariableDescriptor,
internal fun createMutable(descriptor: ValueDescriptor,
isVar: Boolean, value: LLVMValueRef? = null, variableLocation: VariableDebugLocation?) : Int {
assert(!contextVariablesToIndex.contains(descriptor))
val index = variables.size
@@ -76,6 +88,20 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
return index
}
internal var skip = 0
internal fun createParameter(descriptor: ValueDescriptor, value: LLVMValueRef? = null, variableLocation: VariableDebugLocation?) : Int {
assert(!contextVariablesToIndex.contains(descriptor))
val index = variables.size
val type = functionGenerationContext.getLLVMType(descriptor.type)
val slot = functionGenerationContext.alloca(type, "p-${descriptor.name.asString()}", variableLocation)
val isObject = functionGenerationContext.isObjectType(type)
variables.add(ParameterRecord(slot, isObject))
contextVariablesToIndex[descriptor] = index
if (isObject)
skip++
return index
}
// Creates anonymous mutable variable.
// Think of slot reuse.
fun createAnonymousSlot(value: LLVMValueRef? = null) : LLVMValueRef {
@@ -92,7 +118,7 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
return index
}
fun createImmutable(descriptor: ValueDescriptor, value: LLVMValueRef) : Int {
internal fun createImmutable(descriptor: ValueDescriptor, value: LLVMValueRef) : Int {
if (contextVariablesToIndex.containsKey(descriptor))
throw Error("$descriptor is already defined")
val index = variables.size
@@ -116,18 +142,36 @@ internal class VariableManager(val functionGenerationContext: FunctionGeneration
fun store(value: LLVMValueRef, index: Int) {
variables[index].store(value)
}
fun debugInfoLocalVariableLocation(functionScope: DIScopeOpaqueRef, diType: DITypeOpaqueRef, name:Name, file: DIFileRef, line: Int, location: DILocationRef?):VariableDebugLocation {
val variableDeclaration = DICreateAutoVariable(
builder = functionGenerationContext.context.debugInfo.builder,
scope = functionScope,
name = name.asString(),
file = file,
line = line,
type = diType)
return VariableDebugLocation(localVariable = variableDeclaration!!, location = location, file = file, line = line)
}
}
internal data class VariableDebugLocation(val localVariable: DILocalVariableRef, val location:DILocationRef?, val file:DIFileRef, val line:Int)
internal data class VariableDebugLocation(val localVariable: DILocalVariableRef, val location:DILocationRef?, val file:DIFileRef, val line:Int)
internal fun debugInfoLocalVariableLocation(builder: DIBuilderRef?,
functionScope: DIScopeOpaqueRef, diType: DITypeOpaqueRef, name:Name, file: DIFileRef, line: Int,
location: DILocationRef?): VariableDebugLocation {
val variableDeclaration = DICreateAutoVariable(
builder = builder,
scope = functionScope,
name = name.asString(),
file = file,
line = line,
type = diType)
return VariableDebugLocation(localVariable = variableDeclaration!!, location = location, file = file, line = line)
}
internal fun debugInfoParameterLocation(builder: DIBuilderRef?,
functionScope: DIScopeOpaqueRef, diType: DITypeOpaqueRef,
name:Name, argNo: Int, file: DIFileRef, line: Int,
location: DILocationRef?): VariableDebugLocation {
val variableDeclaration = DICreateParameterVariable(
builder = builder,
scope = functionScope,
name = name.asString(),
argNo = argNo,
file = file,
line = line,
type = diType)
return VariableDebugLocation(localVariable = variableDeclaration!!, location = location, file = file, line = line)
}
@@ -214,6 +214,16 @@ DILocalVariableRef DICreateAutoVariable(DIBuilderRef builder, DIScopeOpaqueRef s
llvm::unwrap(type)));
}
DILocalVariableRef DICreateParameterVariable(DIBuilderRef builder, DIScopeOpaqueRef scope, const char *name, unsigned argNo, DIFileRef file, unsigned line, DITypeOpaqueRef type) {
return llvm::wrap(llvm::unwrap(builder)->createParameterVariable(
llvm::unwrap(scope),
name,
argNo,
llvm::unwrap(file),
line,
llvm::unwrap(type)));
}
DIExpressionRef DICreateEmptyExpression(DIBuilderRef builder) {
return llvm::wrap(llvm::unwrap(builder)->createExpression());
}
@@ -93,6 +93,7 @@ DISubroutineTypeRef DICreateSubroutineType(DIBuilderRef builder,
unsigned typesCount);
DILocalVariableRef DICreateAutoVariable(DIBuilderRef builder, DIScopeOpaqueRef scope, const char *name, DIFileRef file, unsigned line, DITypeOpaqueRef type);
DILocalVariableRef DICreateParameterVariable(DIBuilderRef builder, DIScopeOpaqueRef scope, const char *name, unsigned argNo, DIFileRef file, unsigned line, DITypeOpaqueRef type);
void DIInsertDeclaration(DIBuilderRef builder, LLVMValueRef value, DILocalVariableRef localVariable, DILocationRef location, LLVMBasicBlockRef bb, int64_t *expr, uint64_t exprCount);
DIExpressionRef DICreateEmptyExpression(DIBuilderRef builder);
void DIFunctionAddSubprogram(LLVMValueRef fn, DISubprogramRef sp);
+5 -5
View File
@@ -1080,13 +1080,13 @@ void UpdateRef(ObjHeader** location, const ObjHeader* object) {
}
}
void EnterFrame(ObjHeader** start, int count) {
MEMORY_LOG("EnterFrame %p .. %p\n", start, start + count)
void EnterFrame(ObjHeader** start, int parameters, int count) {
MEMORY_LOG("EnterFrame %p .. %p\n", start, start + count + parameters)
}
void LeaveFrame(ObjHeader** start, int count) {
MEMORY_LOG("LeaveFrame %p .. %p\n", start, start + count)
ReleaseRefs(start + kFrameOverlaySlots, count - kFrameOverlaySlots);
void LeaveFrame(ObjHeader** start, int parameters, int count) {
MEMORY_LOG("LeaveFrame %p .. %p\n", start, start + count + parameters)
ReleaseRefs(start + parameters + kFrameOverlaySlots, count - kFrameOverlaySlots - parameters);
if (*start != nullptr) {
auto arena = initedArena(start);
MEMORY_LOG("LeaveFrame: free arena %p\n", arena)
+2 -2
View File
@@ -362,9 +362,9 @@ void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) RUNTIME_NO
// Optimization: release all references in range.
void ReleaseRefs(ObjHeader** start, int count) RUNTIME_NOTHROW;
// Called on frame enter, if it has object slots.
void EnterFrame(ObjHeader** start, int count) RUNTIME_NOTHROW;
void EnterFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
// Called on frame leave, if it has object slots.
void LeaveFrame(ObjHeader** start, int count) RUNTIME_NOTHROW;
void LeaveFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
// Tries to use returnSlot's arena for allocation.
ObjHeader** GetReturnSlotIfArena(ObjHeader** returnSlot, ObjHeader** localSlot) RUNTIME_NOTHROW;
// Tries to use param's arena for allocation.