diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 380d1a8504e..bc29fa95e77 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -138,7 +138,20 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { return LLVMBuildAlloca(builder, type, name)!! } } - fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildLoad(builder, value, name)!! + fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef { + val result = LLVMBuildLoad(builder, value, name)!! + // Use loadSlot() API for that. + assert(!isObjectRef(value)) + return result + } + fun loadSlot(address: LLVMValueRef, isVar: Boolean, name: String = "") : LLVMValueRef { + val value = LLVMBuildLoad(builder, address, name)!! + if (isObjectRef(value) && isVar) { + val slot = alloca(LLVMTypeOf(value)) + storeAnyLocal(value, slot) + } + return value + } fun store(value: LLVMValueRef, ptr: LLVMValueRef) { // Use updateRef() or storeAny() API for that. assert(!isObjectRef(value)) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index e9d0482630c..647c27eee0b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -670,7 +670,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val bbInit = codegen.basicBlock("label_init") val bbExit = codegen.basicBlock("label_continue") val onePtr = codegen.intToPtr(kImmInt64One, codegen.kObjHeaderPtr) - val objectVal = codegen.load(objectPtr) + val objectVal = codegen.loadSlot(objectPtr, false) val condition = codegen.ucmpGt(objectVal, onePtr) codegen.condBr(condition, bbExit, bbInit) @@ -938,7 +938,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val exceptionPtrPtr = bitcast(codegen.kObjHeaderPtrPtr, exceptionRawPtr, "") // Pointer to Kotlin exception object: - val exceptionPtr = load(exceptionPtrPtr, "exception") + // We do need a slot here, as otherwise exception instance could be freed by _cxa_end_catch. + val exceptionPtr = loadSlot(exceptionPtrPtr, true, "exception") // __cxa_end_catch performs some C++ cleanup, including calling `KotlinException` class destructor. val endCatch = externalFunction("__cxa_end_catch", functionType(voidType, false)) @@ -1343,11 +1344,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid context.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null) { val thisPtr = instanceFieldAccessReceiver(value) - return codegen.load(fieldPtrOfClass(thisPtr, value.descriptor)) + return codegen.loadSlot( + fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) } else { val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!! - return codegen.load(ptr) + return codegen.loadSlot(ptr, value.descriptor.isVar()) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt index 45ddd696909..417c3c13f5e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt @@ -10,36 +10,19 @@ internal class VariableManager(val codegen: CodeGenerator) { fun load() : LLVMValueRef fun store(value: LLVMValueRef) fun address() : LLVMValueRef - fun isRefSlot() : Boolean } - inner class SlotRecord(val address: LLVMValueRef, val refSlot: Boolean) : Record { - override fun load() : LLVMValueRef { - return codegen.load(address) - } - override fun store(value: LLVMValueRef) { - codegen.storeAnyLocal(value, address) - } - override fun address() : LLVMValueRef { - return this.address - } - override fun isRefSlot() = this.refSlot - + inner class SlotRecord(val address: LLVMValueRef, val refSlot: Boolean, val isVar: Boolean) : Record { + override fun load() : LLVMValueRef = codegen.loadSlot(address, isVar) + override fun store(value: LLVMValueRef) = codegen.storeAnyLocal(value, address) + override fun address() : LLVMValueRef = this.address override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}" } class ValueRecord(val value: LLVMValueRef, val descriptor: ValueDescriptor) : Record { - override fun load() : LLVMValueRef { - return value - } - override fun store(value: LLVMValueRef) { - throw Error("writing to immutable: ${descriptor}") - } - override fun address() : LLVMValueRef { - throw Error("no address for: ${descriptor}") - } - override fun isRefSlot() : Boolean = false - + override fun load() : LLVMValueRef = value + override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${descriptor}") + override fun address() : LLVMValueRef = throw Error("no address for: ${descriptor}") override fun toString() = "value of ${value} from ${descriptor}" } @@ -55,14 +38,18 @@ internal class VariableManager(val codegen: CodeGenerator) { fun createVariable(scoped: Pair, value: LLVMValueRef? = null) : Int { // Note that we always create slot for object references for memory management. val descriptor = scoped.first - if (descriptor.isVar() || codegen.isObjectType(codegen.getLLVMType(descriptor.type)) || true) { - return createMutable(scoped, value) - } else { - return createImmutable(scoped, value!!) - } + if (!descriptor.isVar && value != null) + return createImmutable(scoped, 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(scoped, descriptor.isVar, value) } - fun createMutable(scoped: Pair, value: LLVMValueRef? = null) : Int { + fun createMutable(scoped: Pair, + isVar: Boolean, value: LLVMValueRef? = null) : Int { val descriptor = scoped.first assert(descriptors.get(scoped) == null) val index = variables.size @@ -70,7 +57,7 @@ internal class VariableManager(val codegen: CodeGenerator) { val slot = codegen.alloca(type, descriptor.name.asString()) if (value != null) codegen.storeAnyLocal(value, slot) - variables.add(SlotRecord(slot, codegen.isObjectType(type))) + variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar)) descriptors[scoped] = index return index } @@ -91,7 +78,7 @@ internal class VariableManager(val codegen: CodeGenerator) { val slot = codegen.alloca(type) if (value != null) codegen.storeAnyLocal(value, slot) - variables.add(SlotRecord(slot, codegen.isObjectType(type))) + variables.add(SlotRecord(slot, codegen.isObjectType(type), true)) return index } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index c9bcc050469..8d96996c315 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -774,22 +774,18 @@ task expression_as_statement(type: RunKonanTest) { } task memory_var1(type: RunKonanTest) { - disabled = true source = "runtime/memory/var1.kt" } task memory_var2(type: RunKonanTest) { - disabled = true source = "runtime/memory/var2.kt" } task memory_var3(type: RunKonanTest) { - disabled = true source = "runtime/memory/var3.kt" } task memory_var4(type: RunKonanTest) { - disabled = true source = "runtime/memory/var4.kt" }