Improve code quality by explicit static container reference. (#1664)
This commit is contained in:
+2
@@ -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()
|
||||
|
||||
-2
@@ -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())
|
||||
|
||||
+16
@@ -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>()
|
||||
|
||||
+31
-12
@@ -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
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -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")
|
||||
|
||||
|
||||
+21
-19
@@ -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)
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)());
|
||||
|
||||
Reference in New Issue
Block a user