CODEGEN: implementation of global fields initialization.

This commit is contained in:
Konstantin Anisimov
2016-12-15 17:51:50 +03:00
committed by vvlevchenko
parent bca03288f6
commit 86bb5477c5
7 changed files with 215 additions and 29 deletions
@@ -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<PropertyDescriptor> = 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
@@ -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<LLVMValueRef>()
val staticInitializers = mutableListOf<LLVMValueRef>()
val fileInitializers = mutableListOf<IrElement>()
}
@@ -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<ParameterDescriptor, LLVMValueRef> {
private fun bindParameters(descriptor: FunctionDescriptor?): Map<ParameterDescriptor, LLVMValueRef> {
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<LLVMValueRef>) {
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()
}
}
}
@@ -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
@@ -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
+6
View File
@@ -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"
}
+27
View File
@@ -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.