Additional RTTI for debugging. (#1568)
This commit is contained in:
+15
-31
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
+71
-14
@@ -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)
|
||||
|
||||
|
||||
+1
@@ -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")
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user