Additional RTTI for debugging. (#1568)

This commit is contained in:
Nikolay Igotti
2018-05-09 03:36:57 +02:00
committed by GitHub
parent fff09927fd
commit 93820ba5bb
9 changed files with 224 additions and 93 deletions
@@ -351,47 +351,31 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
printBitCode()
}
fun shouldVerifyDescriptors(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.VERIFY_DESCRIPTORS)
}
fun shouldVerifyDescriptors() = config.configuration.getBoolean(KonanConfigKeys.VERIFY_DESCRIPTORS)
fun shouldVerifyIr(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.VERIFY_IR)
}
fun shouldVerifyIr() = config.configuration.getBoolean(KonanConfigKeys.VERIFY_IR)
fun shouldVerifyBitCode(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)
}
fun shouldVerifyBitCode() = config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)
fun shouldPrintDescriptors(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.PRINT_DESCRIPTORS)
}
fun shouldPrintDescriptors() = config.configuration.getBoolean(KonanConfigKeys.PRINT_DESCRIPTORS)
fun shouldPrintIr(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.PRINT_IR)
}
fun shouldPrintIr() = config.configuration.getBoolean(KonanConfigKeys.PRINT_IR)
fun shouldPrintIrWithDescriptors(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.PRINT_IR_WITH_DESCRIPTORS)
}
fun shouldPrintIrWithDescriptors()=
config.configuration.getBoolean(KonanConfigKeys.PRINT_IR_WITH_DESCRIPTORS)
fun shouldPrintBitCode(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.PRINT_BITCODE)
}
fun shouldPrintBitCode() = config.configuration.getBoolean(KonanConfigKeys.PRINT_BITCODE)
fun shouldPrintLocations(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.PRINT_LOCATIONS)
}
fun shouldPrintLocations() = config.configuration.getBoolean(KonanConfigKeys.PRINT_LOCATIONS)
fun shouldProfilePhases(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.TIME_PHASES)
}
fun shouldProfilePhases() = config.configuration.getBoolean(KonanConfigKeys.TIME_PHASES)
fun shouldContainDebugInfo(): Boolean {
return config.configuration.getBoolean(KonanConfigKeys.DEBUG)
}
fun shouldContainDebugInfo() = config.configuration.getBoolean(KonanConfigKeys.DEBUG)
fun shouldGenerateTestRunner(): Boolean = config.configuration.getBoolean(KonanConfigKeys.GENERATE_TEST_RUNNER)
fun shouldOptimize() = config.configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)
fun shouldGenerateTestRunner() =
config.configuration.getBoolean(KonanConfigKeys.GENERATE_TEST_RUNNER)
override fun log(message: () -> String) {
if (phase?.verbose ?: false) {
@@ -33,7 +33,7 @@ internal class LinkStage(val context: Context) {
private val platform = context.config.platform
private val linker = platform.linker
private val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
private val optimize = context.shouldOptimize()
private val debug = config.get(KonanConfigKeys.DEBUG) ?: false
private val linkerOutput = when (context.config.produce) {
CompilerOutputKind.DYNAMIC, CompilerOutputKind.FRAMEWORK -> LinkerOutputKind.DYNAMIC_LIBRARY
@@ -416,7 +416,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
LLVMAddFunction(context.llvmModule, symbolName, llvmFunctionType)!!
}
if (!context.config.configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)) {
// TODO: do we still need it?
if (!context.shouldOptimize()) {
LLVMAddTargetDependentFunctionAttr(llvmFunction, "no-frame-pointer-elim", "true")
}
@@ -50,6 +50,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fieldsCount: Int,
packageName: String?,
relativeName: String?,
extendedInfo: ConstPointer,
writableTypeInfo: ConstPointer?) :
Struct(
@@ -77,6 +78,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
kotlinStringLiteral(packageName),
kotlinStringLiteral(relativeName),
extendedInfo,
*listOfNotNull(writableTypeInfo).toTypedArray()
)
@@ -97,22 +100,36 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}
private val arrayClasses = mapOf(
"kotlin.Array" to -LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt(),
"kotlin.ByteArray" to -1,
"kotlin.CharArray" to -2,
"kotlin.ShortArray" to -2,
"kotlin.IntArray" to -4,
"kotlin.LongArray" to -8,
"kotlin.FloatArray" to -4,
"kotlin.DoubleArray" to -8,
"kotlin.BooleanArray" to -1,
"kotlin.String" to -2,
"konan.ImmutableBinaryBlob" to -1
"kotlin.Array" to kObjHeaderPtr,
"kotlin.ByteArray" to LLVMInt8Type()!!,
"kotlin.CharArray" to LLVMInt16Type()!!,
"kotlin.ShortArray" to LLVMInt16Type()!!,
"kotlin.IntArray" to LLVMInt32Type()!!,
"kotlin.LongArray" to LLVMInt64Type()!!,
"kotlin.FloatArray" to LLVMFloatType()!!,
"kotlin.DoubleArray" to LLVMDoubleType()!!,
"kotlin.BooleanArray" to LLVMInt8Type()!!,
"kotlin.String" to LLVMInt16Type()!!,
"konan.ImmutableBinaryBlob" to LLVMInt8Type()!!
)
// Keep in sync with Konan_RuntimeType.
private val runtimeTypeMap = mapOf(
kObjHeaderPtr to 1,
LLVMInt8Type()!! to 2,
LLVMInt16Type()!! to 3,
LLVMInt32Type()!! to 4,
LLVMInt64Type()!! to 5,
LLVMFloatType()!! to 6,
LLVMDoubleType()!! to 7,
kInt8Ptr to 8,
LLVMInt1Type()!! to 9
)
private fun getInstanceSize(classType: LLVMTypeRef?, className: FqName) : Int {
val arraySize = arrayClasses.get(className.asString());
if (arraySize != null) return arraySize;
val elementType = arrayClasses.get(className.asString())
// Check if it is an array.
if (elementType != null) return -LLVMABISizeOfType(llvmTargetData, elementType).toInt()
return LLVMStoreSizeOfType(llvmTargetData, classType).toInt()
}
@@ -180,6 +197,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
reflectionInfo.packageName,
reflectionInfo.relativeName,
makeExtendedInfo(classDesc),
llvmDeclarations.writableTypeInfoGlobal?.pointer
)
@@ -225,7 +243,45 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
}.sortedBy { it.nameSignature.value }
}
// TODO: extract more code common with generate()
private fun mapRuntimeType(type: LLVMTypeRef): Int =
runtimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
private fun makeExtendedInfo(descriptor: ClassDescriptor): ConstPointer {
// TODO: shall we actually do that?
if (context.shouldOptimize())
return NullPointer(runtime.extendedTypeInfoType)
val className = descriptor.fqNameSafe.toString()
val llvmDeclarations = context.llvmDeclarations.forClass(descriptor)
val bodyType = llvmDeclarations.bodyType
val elementType = arrayClasses[className]
val value = if (elementType != null) {
// An array type.
val runtimeElementType = mapRuntimeType(elementType)
Struct(runtime.extendedTypeInfoType,
Int32(-runtimeElementType),
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr))
} else {
data class FieldRecord(val offset: Int, val type: Int, val name: String)
val fields = getStructElements(bodyType).mapIndexedNotNull { index, type ->
FieldRecord(
LLVMOffsetOfElement(llvmTargetData, bodyType, index).toInt(),
mapRuntimeType(type),
llvmDeclarations.fields[index].name.asString())
}
val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", int32Type,
fields.map { Int32(it.offset) })
val typesPtr = staticData.placeGlobalConstArray("kexttype:$className", int8Type,
fields.map { Int8(it.type.toByte()) })
val namesPtr = staticData.placeGlobalConstArray("kextname:$className", kInt8Ptr,
fields.map { staticData.placeCStringLiteral(it.name) })
Struct(runtime.extendedTypeInfoType, Int32(fields.size), offsetsPtr, typesPtr, namesPtr)
}
val result = staticData.placeGlobal("", value)
return result.pointer
}
// TODO: extract more code common with generate().
fun generateSyntheticInterfaceImpl(
descriptor: ClassDescriptor,
methodImpls: Map<FunctionDescriptor, ConstPointer>
@@ -283,6 +339,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fields = fieldsPtr, fieldsCount = fieldsCount,
packageName = reflectionInfo.packageName,
relativeName = reflectionInfo.relativeName,
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
writableTypeInfo = writableTypeInfo
), vtable)
@@ -34,6 +34,7 @@ class Runtime(bitcodeFile: String) {
?: throw Error("struct.$name is not found in the Runtime module.")
val typeInfoType = getStructType("TypeInfo")
val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
val fieldTableRecordType = getStructType("FieldTableRecord")
val methodTableRecordType = getStructType("MethodTableRecord")
@@ -911,7 +911,7 @@ internal object Devirtualization {
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val nativePtrType = context.builtIns.nativePtr.defaultType
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue.single { it.descriptor.valueParameters[0].type == nativePtrType }
val optimize = context.config.configuration.get(KonanConfigKeys.OPTIMIZATION) ?: false
val optimize = context.shouldOptimize()
irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression {
+90 -28
View File
@@ -32,6 +32,19 @@ namespace {
char debugBuffer[4096];
constexpr int runtimeTypeSize[] = {
-1, // INVALID
sizeof(ObjHeader*), // OBJECT
1, // INT8
2, // INT16
4, // INT32
8, // INT64
4, // FLOAT32
8, // FLOAT64
sizeof(void*), // NATIVE_PTR
1 // BOOLEAN
};
} // namespace
extern "C" {
@@ -63,39 +76,88 @@ RUNTIME_USED KInt Konan_DebugPrint(KRef obj) {
return 0;
}
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) {
if (obj == nullptr || index < 0) return Konan_RuntimeType::INVALID;
auto typeInfo = obj->type_info();
if (typeInfo->instanceSize_ < 0) {
// Arrays.
if (index >= obj->array()->count_) return Konan_RuntimeType::INVALID;
if (typeInfo == theArrayTypeInfo) {
return Konan_RuntimeType::OBJECT;
}
// TODO: support other array types.
return Konan_RuntimeType::INVALID;
}
RUNTIME_USED int Konan_DebugIsArray(KRef obj) {
return obj == nullptr || IsArray(obj) ? 1 : 0;
}
// TODO: support primitive type fields as well!
if (index >= typeInfo->objOffsetsCount_) return Konan_RuntimeType::INVALID;
return Konan_RuntimeType::OBJECT;
RUNTIME_USED int Konan_DebugGetFieldCount(KRef obj) {
if (obj == nullptr)
return 0;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return 0;
if (IsArray(obj))
return obj->array()->count_;
return extendedTypeInfo->fieldsCount_;
}
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) {
if (obj == nullptr || index < 0)
return Konan_RuntimeType::RT_INVALID;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return Konan_RuntimeType::RT_INVALID;
if (extendedTypeInfo->fieldsCount_ < 0)
return -typeInfo->fieldsCount_;
if (index >= extendedTypeInfo->fieldsCount_)
return Konan_RuntimeType::RT_INVALID;
return extendedTypeInfo->fieldTypes_[index];
}
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index) {
if (obj == nullptr || index < 0) return nullptr;
auto typeInfo = obj->type_info();
if (typeInfo->instanceSize_ < 0) {
// Arrays.
if (index >= obj->array()->count_) return nullptr;
if (typeInfo == theArrayTypeInfo) {
return ArrayAddressOfElementAt(obj->array(), index);
}
// TODO: support other array types.
if (obj == nullptr || index < 0)
return nullptr;
}
// TODO: support primitive type fields as well!
if (index >= typeInfo->objOffsetsCount_) return nullptr;
return reinterpret_cast<uint8_t*>(obj + 1) + typeInfo->objOffsets_[index];
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
if (extendedTypeInfo->fieldsCount_ < 0) {
if (index >= obj->array()->count_)
return nullptr;
return reinterpret_cast<uint8_t*>(obj + 1) + index * runtimeTypeSize[-typeInfo->fieldsCount_];
}
if (index >= extendedTypeInfo->fieldsCount_)
return nullptr;
return reinterpret_cast<uint8_t*>(obj + 1) + extendedTypeInfo->fieldOffsets_[index];
}
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED const char* Konan_DebugGetFieldName(KRef obj, int index) {
if (obj == nullptr || index < 0)
return nullptr;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
// For arrays, field name makes not much sense.
if (extendedTypeInfo->fieldsCount_ < 0)
return "";
if (index >= extendedTypeInfo->fieldsCount_)
return nullptr;
return extendedTypeInfo->fieldNames_[index];
}
} // extern "C"
+12 -17
View File
@@ -24,18 +24,6 @@
#ifndef KONAN_NO_DEBUG_API
// Type for runtime representation of Konan object.
enum Konan_RuntimeType {
INVALID = 0,
OBJECT = 1,
INT8 = 2,
INT16 = 3,
INT32 = 4,
INT64 = 5,
FLOAT32 = 6,
FLOAT64 = 7
};
#ifdef __cplusplus
extern "C" {
#endif
@@ -46,25 +34,32 @@ char* Konan_DebugBuffer();
// Get size of memory buffer where debugger can put data in Konan app process.
RUNTIME_USED
int Konan_DebugBufferSize();
int32_t Konan_DebugBufferSize();
// Put string representation of an object to the provided buffer.
RUNTIME_USED
int Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int bufferSize);
int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int bufferSize);
// Print to console string representation of an object.
RUNTIME_USED
int Konan_DebugPrint(KRef obj);
int32_t Konan_DebugPrint(KRef obj);
// Returns 1 if obj refers to an array, string or binary blob and 0 otherwise.
RUNTIME_USED int Konan_DebugIsArray(KRef obj);
// Returns number of fields in an objects, or elements in an array.
RUNTIME_USED int Konan_DebugGetFieldCount(KRef obj);
// Compute type of field or an array element at the index, or 0, if incorrect,
// see Konan_RuntimeType.
// TODO: currently, only object fields are supported, will be fixed soon.
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index);
// Compute address of field or an array element at the index, or null, if incorrect.
// TODO: currently, only object fields are supported, will be fixed soon.
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index);
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED const char* Konan_DebugGetFieldName(KRef obj, int index);
#ifdef __cplusplus
}
#endif
+31
View File
@@ -42,6 +42,34 @@ struct FieldTableRecord {
int fieldOffset_;
};
// Type for runtime representation of Konan object.
// Keep in sync with runtimeTypeMap in RTTIGenerator.
enum Konan_RuntimeType {
RT_INVALID = 0,
RT_OBJECT = 1,
RT_INT8 = 2,
RT_INT16 = 3,
RT_INT32 = 4,
RT_INT64 = 5,
RT_FLOAT32 = 6,
RT_FLOAT64 = 7,
RT_NATIVE_PTR = 8,
RT_BOOLEAN = 9
};
// Extended information about a type.
struct ExtendedTypeInfo {
// Number of fields (negated Konan_RuntimeType for array types).
int32_t fieldsCount_;
// Offsets of all fields.
const int32_t* fieldOffsets_;
// Types of all fields.
const uint8_t* fieldTypes_;
// Names of all fields.
const char** fieldNames_;
// TODO: do we want any other info here?
};
// This struct represents runtime type information and by itself is the compile time
// constant.
struct TypeInfo {
@@ -74,6 +102,9 @@ struct TypeInfo {
// or `null` if the class is anonymous.
ObjHeader* relativeName_;
// Extended RTTI.
const ExtendedTypeInfo* extendedInfo_;
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
WritableTypeInfo* writableInfo_;
#endif