Improve code quality by explicit static container reference. (#1664)

This commit is contained in:
Nikolay Igotti
2018-06-07 17:01:54 +03:00
committed by GitHub
parent 686d60a908
commit 4bc12573f2
14 changed files with 114 additions and 57 deletions
@@ -114,6 +114,8 @@ val IrFunction.isSuspend get() = this is IrSimpleFunction && this.isSuspend
fun IrClass.isUnit() = this.fqNameSafe == KotlinBuiltIns.FQ_NAMES.unit.toSafe()
fun IrClass.isKotlinArray() = this.fqNameSafe == KotlinBuiltIns.FQ_NAMES.array.toSafe()
fun IrClass.getSuperClassNotAny() = this.superClasses.map { it.owner }.atMostOne { !it.isInterface && !it.isAny() }
fun IrClass.isAny() = this.fqNameSafe == KotlinBuiltIns.FQ_NAMES.any.toSafe()
@@ -278,8 +278,6 @@ internal val ClassDescriptor.writableTypeInfoSymbolName: String
return "ktypew:" + this.fqNameSafe.toString()
}
internal val theUnitInstanceName = "kobj:kotlin.Unit"
internal val ClassDescriptor.objectInstanceFieldSymbolName: String
get() {
assert (this.isExported())
@@ -272,6 +272,18 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
return function
}
private fun importGlobal(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
if (LLVMGetNamedGlobal(llvmModule, name) != null) {
throw IllegalArgumentException("global $name already exists")
}
val externalGlobal = LLVMGetNamedGlobal(otherModule, name)!!
val globalType = getGlobalType(externalGlobal)
val global = LLVMAddGlobal(llvmModule, globalType, name)!!
return global
}
private fun copyFunctionAttributes(source: LLVMValueRef, destination: LLVMValueRef) {
// TODO: consider parameter attributes
val attributeIndex = LLVMAttributeFunctionIndex
@@ -386,6 +398,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
private fun importRtGlobal(name: String) = importGlobal(name, runtime.llvmModule)
val allocInstanceFunction = importRtFunction("AllocInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val initInstanceFunction = importRtFunction("InitInstance")
@@ -457,6 +471,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
origin = context.standardLlvmSymbolsOrigin
)
val staticContainer = importRtGlobal("theStaticObjectsContainer")
val memsetFunction = importMemset()
val usedFunctions = mutableListOf<LLVMValueRef>()
@@ -33,10 +33,15 @@ internal fun createLlvmDeclarations(context: Context): LlvmDeclarations {
context.ir.irModule.acceptChildrenVoid(generator)
return with(generator) {
LlvmDeclarations(
functions, classes, fields, staticFields, theUnitInstanceRef
functions, classes, fields, staticFields, uniques
)
}
}
// Please note, that llvmName is part of the ABI, and cannot be liberally changed.
enum class UniqueKind(val llvmName: String) {
UNIT("theUnitInstance"),
EMPTY_ARRAY("theEmptyArray")
}
internal class LlvmDeclarations(
@@ -44,8 +49,7 @@ internal class LlvmDeclarations(
private val classes: Map<ClassDescriptor, ClassLlvmDeclarations>,
private val fields: Map<IrField, FieldLlvmDeclarations>,
private val staticFields: Map<IrField, StaticFieldLlvmDeclarations>,
private val theUnitInstanceRef: ConstPointer?
) {
private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
fun forFunction(descriptor: FunctionDescriptor) = functions[descriptor] ?:
error(descriptor.toString())
@@ -61,7 +65,7 @@ internal class LlvmDeclarations(
fun forSingleton(descriptor: ClassDescriptor) = forClass(descriptor).singletonDeclarations ?:
error(descriptor.toString())
fun getUnitInstanceRef() = theUnitInstanceRef ?: error("")
fun forUnique(kind: UniqueKind) = unique[kind] ?: error("No unique $kind")
}
@@ -88,6 +92,8 @@ internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMType
internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
internal class UniqueLlvmDeclarations(val pointer: ConstPointer)
// TODO: rework getFields and getDeclaredFields.
/**
@@ -151,7 +157,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
val classes = mutableMapOf<ClassDescriptor, ClassLlvmDeclarations>()
val fields = mutableMapOf<IrField, FieldLlvmDeclarations>()
val staticFields = mutableMapOf<IrField, StaticFieldLlvmDeclarations>()
var theUnitInstanceRef: ConstPointer? = null
val uniques = mutableMapOf<UniqueKind, UniqueLlvmDeclarations>()
private class Namer(val prefix: String) {
private val names = mutableMapOf<DeclarationDescriptor, Name>()
@@ -266,8 +272,11 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
typeInfoPtr = typeInfoGlobal.pointer
}
if (descriptor.isUnit() || descriptor.isKotlinArray())
createUniqueDeclarations(descriptor, typeInfoPtr, bodyType)
val singletonDeclarations = if (descriptor.kind.isSingleton) {
createSingletonDeclarations(descriptor, typeInfoPtr, bodyType)
createSingletonDeclarations(descriptor)
} else {
null
}
@@ -296,14 +305,24 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
singletonDeclarations, objCDeclarations)
}
private fun createSingletonDeclarations(
descriptor: ClassDescriptor,
typeInfoPtr: ConstPointer,
bodyType: LLVMTypeRef
): SingletonLlvmDeclarations? {
private fun createUniqueDeclarations(
descriptor: ClassDescriptor, typeInfoPtr: ConstPointer, bodyType: LLVMTypeRef) {
when {
descriptor.isUnit() -> {
uniques[UniqueKind.UNIT] =
UniqueLlvmDeclarations(staticData.createUniqueInstance(UniqueKind.UNIT, bodyType, typeInfoPtr))
}
descriptor.isKotlinArray() -> {
uniques[UniqueKind.EMPTY_ARRAY] =
UniqueLlvmDeclarations(staticData.createUniqueInstance(UniqueKind.EMPTY_ARRAY, bodyType, typeInfoPtr))
}
else -> TODO("Unsupported unique $descriptor")
}
}
private fun createSingletonDeclarations(descriptor: ClassDescriptor): SingletonLlvmDeclarations? {
if (descriptor.isUnit()) {
this.theUnitInstanceRef = staticData.createUnitInstance(bodyType, typeInfoPtr)
return null
}
@@ -43,7 +43,6 @@ class Runtime(bitcodeFile: String) {
val objHeaderType = getStructType("ObjHeader")
val objHeaderPtrType = pointerType(objHeaderType)
val arrayHeaderType = getStructType("ArrayHeader")
val containerHeaderType = getStructType("ContainerHeader")
val frameOverlayType = getStructType("FrameOverlay")
@@ -31,14 +31,15 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.types.replace
private fun StaticData.objHeader(typeInfo: ConstPointer): Struct {
val container = NullPointer(runtime.containerHeaderType) // Static object mark.
val container = constValue(context.llvm.staticContainer)
return Struct(runtime.objHeaderType, typeInfo, container)
}
private fun StaticData.arrayHeader(typeInfo: ConstPointer, length: Int): Struct {
assert (length >= 0)
val container = NullPointer(runtime.containerHeaderType) // Static object mark.
val container = constValue(context.llvm.staticContainer)
return Struct(runtime.arrayHeaderType, typeInfo, container, Int32(length))
}
@@ -134,26 +135,27 @@ internal fun StaticData.createArrayList(array: ConstPointer, length: Int): Const
return createKotlinObject(arrayListClass, body)
}
internal fun StaticData.createUnitInstance(bodyType: LLVMTypeRef,
typeInfo: ConstPointer
): ConstPointer {
internal fun StaticData.createUniqueInstance(
kind: UniqueKind, bodyType: LLVMTypeRef, typeInfo: ConstPointer): ConstPointer {
assert (getStructElements(bodyType).isEmpty())
val objHeader = objHeader(typeInfo)
val global = this.placeGlobal(theUnitInstanceName, objHeader, isExported = true)
val global = this.placeGlobal(kind.llvmName, objHeader, isExported = true)
return global.pointer
}
internal val ContextUtils.theUnitInstanceRef: ConstPointer
get() {
val unitDescriptor = context.ir.symbols.unit.owner
return if (isExternal(unitDescriptor)) {
constPointer(importGlobal(
theUnitInstanceName,
context.llvm.runtime.objHeaderType,
origin = unitDescriptor.llvmSymbolOrigin
))
} else {
context.llvmDeclarations.getUnitInstanceRef()
}
internal fun ContextUtils.unique(kind: UniqueKind): ConstPointer {
val descriptor = when (kind) {
UniqueKind.UNIT -> context.ir.symbols.unit.owner
UniqueKind.EMPTY_ARRAY -> context.ir.symbols.array.owner
}
return if (isExternal(descriptor)) {
constPointer(importGlobal(
kind.llvmName, context.llvm.runtime.objHeaderType, origin = descriptor.llvmSymbolOrigin
))
} else {
context.llvmDeclarations.forUnique(kind).pointer
}
}
internal val ContextUtils.theUnitInstanceRef: ConstPointer
get() = this.unique(UniqueKind.UNIT)
+4 -5
View File
@@ -25,10 +25,6 @@
namespace {
const ArrayHeader anEmptyArray = {
const_cast<TypeInfo*>(theArrayTypeInfo), /* permanent object */ 0, /* element count */ 0
};
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
// TODO: optimize it!
if (thiz->container()->frozen()) {
@@ -56,6 +52,9 @@ inline void copyImpl(KConstRef thiz, KInt fromIndex,
extern "C" {
// Generated as part of Kotlin standard library.
extern const ObjHeader theEmptyArray;
// TODO: those must be compiler intrinsics afterwards.
// Array.kt
@@ -117,7 +116,7 @@ void Kotlin_Array_copyImpl(KConstRef thiz, KInt fromIndex,
// Arrays.kt
OBJ_GETTER0(Kotlin_emptyArray) {
RETURN_OBJ(const_cast<ObjHeader*>(anEmptyArray.obj()));
RETURN_OBJ(const_cast<ObjHeader*>(&theEmptyArray));
}
KByte Kotlin_ByteArray_get(KConstRef thiz, KInt index) {
+16 -1
View File
@@ -19,14 +19,29 @@
#include "Common.h"
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message);
// To avoid cluttering optimized code with asserts, they could be turned off.
#define KONAN_ENABLE_ASSERT 1
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message);
#if KONAN_ENABLE_ASSERT
// Use RuntimeAssert() in internal state checks, which could be ignored in production.
#define RuntimeAssert(condition, message) \
if (!(condition)) { \
RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), message); \
}
#else
#define RuntimeAssert(condition, message)
#endif
// Use RuntimeCheck() in runtime checks that could fail due to external condition and shall lead
// to program termination. Never compiled out.
#define RuntimeCheck(condition, message) \
if (!(condition)) { \
RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), message); \
}
#endif // RUNTIME_ASSERT_H
+4 -4
View File
@@ -63,14 +63,14 @@ struct Backtrace {
auto result = AllocArrayInstance(
theArrayTypeInfo, count - skipCount, arrayHolder.slot());
// TODO: throw cached OOME?
RuntimeAssert(result != nullptr, "Cannot create backtrace array");
RuntimeCheck(result != nullptr, "Cannot create backtrace array");
}
void setNextElement(const char* element) {
auto result = CreateStringFromCString(
element, ArrayAddressOfElementAt(obj()->array(), index++));
// TODO: throw cached OOME?
RuntimeAssert(result != nullptr, "Cannot create backtrace array element");
RuntimeCheck(result != nullptr, "Cannot create backtrace array element");
}
ObjHeader* obj() { return arrayHolder.obj(); }
@@ -144,7 +144,7 @@ OBJ_GETTER0(GetCurrentStackTrace) {
int size = backtrace(buffer, maxSize);
char** symbols = backtrace_symbols(buffer, size);
RuntimeAssert(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
if (size < kSkipFrames)
return AllocArrayInstance(theArrayTypeInfo, 0, OBJ_RESULT);
AutoFree autoFree(symbols);
@@ -166,7 +166,7 @@ void ThrowException(KRef exception) {
"Throwing something non-throwable");
#if KONAN_NO_EXCEPTIONS
PrintThrowable(exception);
RuntimeAssert(false, "Exceptions unsupported");
RuntimeCheck(false, "Exceptions unsupported");
#else
throw ObjHolder(exception);
#endif
+8
View File
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "Porting.h"
#include "Types.h"
typedef KInt Arena;
@@ -27,14 +28,17 @@ extern "C" {
// These functions are implemented in JS file for WASM and are not available on other platforms.
RUNTIME_NORETURN Arena Konan_js_allocateArena() {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_freeArena(Arena arena) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_pushIntToArena(Arena arena, KInt value) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN KInt Konan_js_getInt(Arena arena,
@@ -42,6 +46,7 @@ RUNTIME_NORETURN KInt Konan_js_getInt(Arena arena,
Pointer propertyPtr,
KInt propertyLen) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN KInt Konan_js_getProperty(Arena arena,
@@ -49,6 +54,7 @@ RUNTIME_NORETURN KInt Konan_js_getProperty(Arena arena,
Pointer propertyPtr,
KInt propertyLen) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_setFunction(Arena arena,
@@ -57,6 +63,7 @@ RUNTIME_NORETURN void Konan_js_setFunction(Arena arena,
KInt propertyLength,
KInt function) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_setString(Arena arena,
@@ -66,6 +73,7 @@ RUNTIME_NORETURN void Konan_js_setString(Arena arena,
Pointer stringPtr,
KInt stringLength) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
}; // extern "C"
+7 -6
View File
@@ -39,12 +39,6 @@
// Auto-adjust GC thresholds.
#define GC_ERGONOMICS 1
// TODO: ensure it is read-only.
ContainerHeader ObjHeader::theStaticObjectsContainer = {
CONTAINER_TAG_PERMANENT | CONTAINER_TAG_INCREMENT,
0 /* Object count */
};
namespace {
// Granularity of arena container chunks.
@@ -407,6 +401,13 @@ inline bool isRefCounted(KConstRef object) {
extern "C" {
// Ensure LLVM never throws theStaticObjectsContainer away.
// TODO: although practically const, marking it as such makes LLVM crazy, fix it.
RUNTIME_USED ContainerHeader theStaticObjectsContainer = {
CONTAINER_TAG_PERMANENT | CONTAINER_TAG_INCREMENT,
0 /* Object count */
};
void objc_release(void* ptr);
void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
RUNTIME_NORETURN void ThrowFreezingException();
+1 -3
View File
@@ -200,10 +200,8 @@ struct ObjHeader {
reinterpret_cast<MetaObjHeader*>(typeInfoOrMeta_) : createMetaObject(&typeInfoOrMeta_);
}
static ContainerHeader theStaticObjectsContainer;
ContainerHeader* container() const {
return container_ == nullptr ? &theStaticObjectsContainer : container_;
return container_;
}
// Unsafe cast to ArrayHeader. Use carefully!
+2 -2
View File
@@ -36,8 +36,8 @@
#include "Porting.h"
#if KONAN_WASM || KONAN_ZEPHYR
extern "C" void Konan_abort(const char*);
extern "C" void Konan_exit(int32_t status);
extern "C" RUNTIME_NORETURN void Konan_abort(const char*);
extern "C" RUNTIME_NORETURN void Konan_exit(int32_t status);
#endif
#ifdef KONAN_ZEPHYR
// In Zephyr's Newlib strnlen(3) is not included from string.h by default.
+2 -2
View File
@@ -33,8 +33,8 @@ void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes);
int32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes);
// Process control.
void abort(void);
void exit(int32_t status);
RUNTIME_NORETURN void abort(void);
RUNTIME_NORETURN void exit(int32_t status);
// Thread control.
void onThreadExit(void (*destructor)());