From 86bb5477c5531025e79a5499e3175e6348f00ab7 Mon Sep 17 00:00:00 2001 From: Konstantin Anisimov Date: Thu, 15 Dec 2016 17:51:50 +0300 Subject: [PATCH] CODEGEN: implementation of global fields initialization. --- .../backend/konan/llvm/CodeGenerator.kt | 23 +-- .../kotlin/backend/konan/llvm/ContextUtils.kt | 6 + .../kotlin/backend/konan/llvm/IrToBitcode.kt | 180 +++++++++++++++++- .../kotlin/backend/konan/llvm/LlvmUtils.kt | 1 + .../backend/konan/llvm/StaticObjects.kt | 1 + backend.native/tests/build.gradle | 6 + runtime/src/launcher/cpp/launcher.cpp | 27 +++ 7 files changed, 215 insertions(+), 29 deletions(-) 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 caab9b28b75..b19a8613e3d 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 @@ -18,7 +18,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { val descriptor = declaration.descriptor if (currentFunction == descriptor) return - variableIndex = 0 currentFunction = declaration.descriptor val fn = declaration.descriptor.llvmFunction prologueBb = LLVMAppendBasicBlock(fn, "prologue") @@ -35,15 +34,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { fun fields(descriptor: ClassDescriptor):List = descriptor.fields - fun newVar():String = currentFunction!!.tmpVariable() - - private var variableIndex:Int = 0 - private var FunctionDescriptor.tmpVariableIndex: Int - get() = variableIndex - set(i:Int) { variableIndex = i} - - fun FunctionDescriptor.tmpVariable():String = "tmp_${tmpVariableIndex++}" - private var prologueBb: LLVMBasicBlockRef? = null private var entryBb: LLVMBasicBlockRef? = null @@ -143,14 +133,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p) - fun basicBlock(name: String = "label_"): LLVMBasicBlockRef = - LLVMAppendBasicBlock(currentFunction!!.llvmFunction, name)!! - - fun basicBlock(name: String, code: () -> Unit) = basicBlock(name).apply { - appendingTo(this) { - code() - } - } + fun basicBlock(function: LLVMValueRef, name: String = "label_"): LLVMBasicBlockRef = + LLVMAppendBasicBlock(function, name)!! fun lastBasicBlock(): LLVMBasicBlockRef? = LLVMGetLastBasicBlock(currentFunction!!.llvmFunction) @@ -192,9 +176,10 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { inner class PositionHolder { private val builder: LLVMBuilderRef = LLVMCreateBuilder()!! + fun getBuilder(): LLVMBuilderRef { if (isAfterTerminator) { - positionAtEnd(basicBlock("unreachable")) + positionAtEnd(basicBlock(currentFunction!!.llvmFunction, "unreachable")) } return builder diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index f5e7984d076..88d80950bb4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -8,6 +8,7 @@ import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -190,6 +191,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule) + var globalInitIndex:Int = 0 + val allocInstanceFunction = importRtFunction("AllocInstance") val initInstanceFunction = importRtFunction("InitInstance") val allocArrayFunction = importRtFunction("AllocArrayInstance") @@ -200,5 +203,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val isInstanceFunction = importRtFunction("IsInstance") val checkInstanceFunction = importRtFunction("CheckInstance") val throwExceptionFunction = importRtFunction("ThrowException") + val appendToInitalizersTail = importRtFunction("AppendToInitializersTail") val usedFunctions = mutableListOf() + val staticInitializers = mutableListOf() + val fileInitializers = mutableListOf() } 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 9c01b8b2c2e..892977995dc 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 @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.utils.addToStdlib.singletonList internal fun emitLLVM(context: Context) { @@ -157,6 +158,21 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid * @return the requested value */ fun genGetValue(descriptor: ValueDescriptor, result: String = ""): LLVMValueRef + + /** + * Generates name for local llvm variable. + * + * @return the requested value + */ + fun newVar():String + + /** + * Returns owning function scope. + * + * @return the requested value + */ + fun functionScope(): CodeContext + } /** @@ -165,7 +181,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid * During function code generation [FunctionScope] should be set up. */ private object TopLevelCodeContext : CodeContext { - private fun unsupported(any: Any? = null): Nothing = throw UnsupportedOperationException(any?.toString() ?: "") override fun genReturn(target: CallableDescriptor, value: LLVMValueRef?) = unsupported(target) @@ -183,6 +198,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid override fun getDeclaredVariable(descriptor: VariableDescriptor) = null override fun genGetValue(descriptor: ValueDescriptor, result: String) = unsupported(descriptor) + + override fun newVar(): String = unsupported() + override fun functionScope(): CodeContext = unsupported() } /** @@ -219,9 +237,80 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// override fun visitModuleFragment(module: IrModuleFragment) { logger.log("visitModule : ${ir2string(module)}") - module.acceptChildrenVoid(this) - appendLlvmUsed(context.llvm.usedFunctions) + module.acceptChildrenVoid(this) + appendLlvmUsed(context.llvm.usedFunctions) + appendStaticInitializers(context.llvm.staticInitializers) + } + + //-------------------------------------------------------------------------// + + val kVoidFuncType = LLVMFunctionType(LLVMVoidType(), null, 0, 0) + val kNodeInitType = LLVMGetTypeByName(context.llvmModule, "struct.InitNode")!! + //-------------------------------------------------------------------------// + + fun createInitBody(initName: String) { + val initFunction = LLVMAddFunction(context.llvmModule, initName, kVoidFuncType)!! // create LLVM function + using(FunctionScope(initFunction)) { + val bbEnter = LLVMAppendBasicBlock(initFunction, "label_enter")!! + codegen.positionAtEnd(bbEnter) + + context.llvm.fileInitializers.forEachIndexed { + i, it -> + val irField = it as IrField + val descriptor = irField.descriptor + val initialization = evaluateExpression("\$init_var_$i", irField.initializer) + val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName) + codegen.store(initialization!!, globalPtr!!) + } + codegen.ret(null) + } + } + + //-------------------------------------------------------------------------// + // Creates static struct InitNode $nodeName = {$initName, NULL}; + + fun createInitNode(initName: String, nodeName: String) { + memScoped { + val initFunction = LLVMGetNamedFunction(context.llvmModule, initName)!! // Get initialization function. + val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL. + val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args. + val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode. + context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName". + } + } + + //-------------------------------------------------------------------------// + + fun createInitCtor(ctorName: String, nodeName: String) { + val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function. + val bbEnter = LLVMAppendBasicBlock(ctorFunction, "label_enter")!! // Create basic block. + + codegen.positionAtEnd(bbEnter) + val initNodePtr = LLVMGetNamedGlobal(context.llvmModule, nodeName)!! // Get LLVM function initializing globals of current file. + codegen.call(context.llvm.appendToInitalizersTail, initNodePtr.singletonList(), "") // Add node to the tail of initializers list. + codegen.ret(null) + + context.llvm.staticInitializers.add(ctorFunction) // Push newly created constructor in staticInitializers list. + } + + //-------------------------------------------------------------------------// + + override fun visitFile(declaration: IrFile) { + + context.llvm.fileInitializers.clear() + super.visitFile(declaration) + + if (context.llvm.fileInitializers.isEmpty()) + return + val fileName = declaration.name.takeLastWhile { it != '/' }.dropLastWhile { it != '.' }.dropLast(1) + val initName = "${fileName}_init_${context.llvm.globalInitIndex}" + val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}" + val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}" + + createInitBody(initName) + createInitNode(initName, nodeName) + createInitCtor(ctorName, nodeName) } //-------------------------------------------------------------------------// @@ -470,10 +559,24 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid /** * The [CodeContext] enclosing the entire function body. */ - private inner class FunctionScope(val declaration: IrFunction) : - ParameterScope(bindParameters(declaration.descriptor)) { + private inner class FunctionScope (val declaration: IrFunction?) : + ParameterScope(bindParameters(declaration?.descriptor)) { + constructor(llvmFunction:LLVMValueRef):this(null) { + this.llvmFunction = llvmFunction + } + + var llvmFunction:LLVMValueRef? = declaration?.let{ + codegen.llvmFunction(declaration.descriptor) + } + + var tmpVariableCount = 0 + override fun newVar(): String = "tmp_${tmpVariableCount++}" override fun genReturn(target: CallableDescriptor, value: LLVMValueRef?) { + if (declaration == null) { + codegen.ret(value) + return + } if (target == declaration.descriptor) { codegen.ret(value) } else { @@ -490,12 +593,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid codegen.call(context.llvm.throwExceptionFunction, args, "") codegen.unreachable() } + + override fun functionScope(): CodeContext = this } /** * Binds LLVM function parameters to IR parameter descriptors. */ - private fun bindParameters(descriptor: FunctionDescriptor): Map { + private fun bindParameters(descriptor: FunctionDescriptor?): Map { + if (descriptor == null) return emptyMap() val paramDescriptors = descriptor.allValueParameters assert(paramDescriptors.size == codegen.countParams(descriptor)) @@ -606,12 +712,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val descriptor = expression.descriptor if (descriptor.containingDeclaration is PackageFragmentDescriptor) { val globalProperty = LLVMAddGlobal(context.llvmModule, codegen.getLLVMType(descriptor.type), descriptor.symbolName) - LLVMSetInitializer(globalProperty, evaluateExpression("", expression.initializer)) + if (expression.initializer!!.expression is IrConst<*>) { + LLVMSetInitializer(globalProperty, evaluateExpression("", expression.initializer)) + } else { + LLVMSetInitializer(globalProperty, codegen.kNullObjHeaderPtr) + context.llvm.fileInitializers.add(expression) + } return } - - //super.visitField(expression) - } //-------------------------------------------------------------------------// @@ -1971,4 +2079,56 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid LLVMSetSection(llvmUsedGlobal.llvmGlobal, "llvm.metadata"); } } + + //-------------------------------------------------------------------------// + // Create type { i32, void ()*, i8* } + + val kCtorType = memScoped { + val ctorType = LLVMPointerType(kVoidFuncType, 0) + val typeList = allocArrayOf(LLVMInt32Type(), ctorType, kInt8Ptr)[0].ptr + LLVMStructType(typeList, 3, 0) + }!! + + //-------------------------------------------------------------------------// + // Create object { i32, void ()*, i8* } { i32 1, void ()* @ctorFunction, i8* null } + + fun createGlobalCtor(ctorFunction: LLVMValueRef) = memScoped { + val priority = kImmInt32One + val data = kNullInt8Ptr + val argList = allocArrayOf(priority, ctorFunction, data)[0].ptr + val ctorItem = LLVMConstNamedStruct(kCtorType, argList, 3)!! + constPointer(ctorItem) + } + + //-------------------------------------------------------------------------// + // Append initializers of global variables in "llvm.global_ctors" array + + fun appendStaticInitializers(initializers: List) { + if (initializers.isEmpty()) return + + val ctorList = initializers.map { it -> createGlobalCtor(it) } + val globalCtors = context.llvm.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, ctorList) + LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) + } + + //-------------------------------------------------------------------------// + /** + * This block is devoted to coming codegen refactoring: + * removing [Codegenerator.currentFunction] of [FunctionDesctiptor] + * and working with local variables with [FunctionScope] and other enhancements. + */ + + fun CodeGenerator.newVar() = currentCodeContext.newVar() + + fun CodeGenerator.basicBlock(name: String = "label_"): LLVMBasicBlockRef { + val functionScope:FunctionScope = currentCodeContext.functionScope() as FunctionScope + return basicBlock(functionScope.llvmFunction!!) + } + + fun CodeGenerator.basicBlock(name: String, code: () -> Unit) = basicBlock(name).apply { + appendingTo(this) { + code() + } + } } + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index 044048dd4a6..3310409f737 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -123,6 +123,7 @@ internal val kInt1 = LLVMInt1Type()!! internal val kBoolean = kInt1 internal val kInt8Ptr = pointerType(int8Type) internal val kInt8PtrPtr = pointerType(kInt8Ptr) +internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr) internal val kImmInt32One = Int32(1).llvm internal val kImmInt64One = Int64(1).llvm internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt index 2829fb75bb3..3725e2105f8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.* +import org.jetbrains.kotlin.backend.konan.KonanPlatform import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.expressions.IrConst diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index e3fb13cb5ba..df2b2bbb917 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -753,6 +753,7 @@ task initializers1(type: RunKonanTest) { source = "runtime/basic/initializers1.kt" } + task concatenation(type: RunKonanTest) { goldValue = "Hello world 1 2\nHello, a\nHello, b\n" source = "codegen/basics/concatenation.kt" @@ -823,3 +824,8 @@ task lambda13(type: RunKonanTest) { goldValue = "foo\n" source = "codegen/lambda/lambda13.kt" } + +task initializers2(type: RunKonanTest) { + goldValue = "1\nA\nB\n" + source = "runtime/basic/initializers2.kt" +} diff --git a/runtime/src/launcher/cpp/launcher.cpp b/runtime/src/launcher/cpp/launcher.cpp index 38145e57593..8a0ed935ecf 100644 --- a/runtime/src/launcher/cpp/launcher.cpp +++ b/runtime/src/launcher/cpp/launcher.cpp @@ -3,6 +3,29 @@ #include "Natives.h" #include "Types.h" +//--- Init global variables ---------------------------------------------------// + +typedef void (*Initializer)(); +struct InitNode; + +struct InitNode { + Initializer init; + struct InitNode* next; +}; + +struct InitNode initHeadNode; +struct InitNode* initTailNode = &initHeadNode; + +void InitGlobalVariables() { + struct InitNode *currNode = initHeadNode.next; + while(currNode) { + currNode->init(); + currNode = currNode->next; + } +} + +//--- Setup args --------------------------------------------------------------// + ObjHeader* setupArgs(int argc, char** argv) { // The count is one less, because we skip argv[0] which is the binary name. ObjHeader* args = AllocArrayInstance(theArrayTypeInfo, SCOPE_GLOBAL, argc-1); @@ -15,13 +38,17 @@ ObjHeader* setupArgs(int argc, char** argv) { return args; } +//--- main --------------------------------------------------------------------// + extern "C" void Konan_start(ObjHeader* ); int main(int argc, char** argv) { InitMemory(); + InitGlobalVariables(); ObjHeader* args = setupArgs(argc, argv); + Konan_start(args); // Yes, we have to follow Java convention and return zero.