diff --git a/experiments/student-backend/src/org/kotlinnative/translator/ProjectTranslator.kt b/experiments/student-backend/src/org/kotlinnative/translator/ProjectTranslator.kt new file mode 100644 index 00000000000..8974b5042da --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/ProjectTranslator.kt @@ -0,0 +1,77 @@ +package org.kotlinnative.translator + +import org.jetbrains.kotlin.psi.* +import org.kotlinnative.translator.codegens.ClassCodegen +import org.kotlinnative.translator.codegens.FunctionCodegen +import org.kotlinnative.translator.codegens.ObjectCodegen +import org.kotlinnative.translator.codegens.PropertyCodegen + +class ProjectTranslator(val files: List, + val state: TranslationState) { + private var codeBuilder = state.codeBuilder + + fun generateCode(): String { + with(files) { + map { addClassDeclarations(it) } + map { addObjectDeclarations(it) } + map { addFunctionDeclarations(it) } + map { addPropertyDeclarations(it) } + } + generateProjectBody() + return codeBuilder.toString() + } + + fun addFunctionDeclarations(file: KtFile) { + val variableManager = VariableManager(state.globalVariableCollection) + for (declaration in file.declarations.filterIsInstance()) { + val function = FunctionCodegen(state, variableManager, declaration, codeBuilder) + if (function.external) { + state.externalFunctions.put(function.fullName, function) + } else { + state.functions.put(function.fullName, function) + } + } + } + + fun addClassDeclarations(file: KtFile) { + val variableManager = VariableManager(state.globalVariableCollection) + for (declaration in file.declarations.filterIsInstance()) { + val codegen = ClassCodegen(state, variableManager, declaration, codeBuilder) + state.classes.put(codegen.structName, codegen) + } + } + + fun addPropertyDeclarations(file: KtFile) { + val variableManager = VariableManager(state.globalVariableCollection) + for (declaration in file.declarations.filterIsInstance()) { + val property = PropertyCodegen(state, variableManager, declaration, codeBuilder) + state.properties.put(declaration.name!!, property) + } + } + + fun addObjectDeclarations(file: KtFile) { + val variableManager = VariableManager(state.globalVariableCollection) + for (declaration in file.declarations.filterIsInstance()) { + val codegen = ObjectCodegen(state, variableManager, declaration, codeBuilder) + state.objects.put(codegen.structName, codegen) + } + } + + private fun generateProjectBody() { + with(state) { + properties.values.map { it.generate() } + objects.values.map { it.prepareForGenerate() } + classes.values.map { it.prepareForGenerate() } + objects.values.map { it.generate() } + classes.values.map { it.generate() } + externalFunctions.values.map { it.generate() } + functions.values.filter { it.isExtensionDeclaration }.map { it.generate() } + functions.values.filter { !it.isExtensionDeclaration }.map { it.generate() } + } + + if (state.mainFunction != "main") { + codeBuilder.declareEntryPoint(state.mainFunction) + } + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/TranslationState.kt b/experiments/student-backend/src/org/kotlinnative/translator/TranslationState.kt new file mode 100644 index 00000000000..005d1ca3ffb --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/TranslationState.kt @@ -0,0 +1,117 @@ +package org.kotlinnative.translator + +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.config.getModuleName +import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.config.addKotlinSourceRoots +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.utils.PathUtil +import org.kotlinnative.translator.codegens.ClassCodegen +import org.kotlinnative.translator.codegens.FunctionCodegen +import org.kotlinnative.translator.codegens.ObjectCodegen +import org.kotlinnative.translator.codegens.PropertyCodegen +import org.kotlinnative.translator.exceptions.TranslationException +import org.kotlinnative.translator.llvm.LLVMBuilder +import org.kotlinnative.translator.llvm.LLVMVariable +import java.util.* + + +class TranslationState +private constructor +( + val environment: KotlinCoreEnvironment, + val bindingContext: BindingContext, + val mainFunction: String, arm: Boolean +) { + var externalFunctions = HashMap() + var functions = HashMap() + var classes = HashMap() + var objects = HashMap() + var properties = HashMap() + val codeBuilder = LLVMBuilder(arm) + val extensionFunctions = HashMap>() + val globalVariableCollection = HashMap() + + init { + POINTER_ALIGN = if (arm) 4 else 8 + POINTER_SIZE = if (arm) 4 else 8 + } + + companion object { + var POINTER_ALIGN = 4 + var POINTER_SIZE = 4 + + fun createTranslationState(sources1: List, disposer: Disposable, mainFunction: String, arm: Boolean = false): TranslationState { + + val configuration = CompilerConfiguration() + val messageCollector = object : MessageCollector { + private var hasError = false + + override fun hasErrors(): Boolean = hasError + + override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) { + if (severity.isError) { + System.err.println("[${severity.toString()}]${location.path} ${location.line}:${location.column} $message") + hasError = true + } + } + } + + configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) + configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME) + + configuration.addKotlinSourceRoots(sources) + + val environment = KotlinCoreEnvironment.createForProduction(disposer, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) + val bindingContext = analyze(environment)?.bindingContext ?: throw TranslationException("Can't initialize binding context for project") + + return TranslationState(environment, bindingContext, mainFunction, arm) + } + + private fun analyze(environment: KotlinCoreEnvironment): AnalysisResult? { + val collector = environment.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) + + val analyzer = AnalyzerWithCompilerReport(collector) + analyzer.analyzeAndReport(environment.getSourceFiles(), object : AnalyzerWithCompilerReport.Analyzer { + override fun analyze(): AnalysisResult { + val sharedTrace = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace() + val moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(environment.project, environment.getModuleName()) + + return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationWithCustomContext( + moduleContext, + environment.getSourceFiles(), + sharedTrace, + environment.configuration.get(JVMConfigurationKeys.MODULES), + environment.configuration.get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS), + JvmPackagePartProvider(environment)) + } + + override fun reportEnvironmentErrors() { + val files = environment.configuration.jvmClasspathRoots + val runtimes = files.map { it.canonicalFile }.filter { it.name == PathUtil.KOTLIN_JAVA_RUNTIME_JAR && it.exists() } + collector.report(CompilerMessageSeverity.ERROR, runtimes.joinToString { it.path }, CompilerMessageLocation.NO_LOCATION) + System.err.println(runtimes.joinToString { it.toString() }) + } + }) + + return if (analyzer.hasErrors()) null else analyzer.analysisResult + } + + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/VariableManager.kt b/experiments/student-backend/src/org/kotlinnative/translator/VariableManager.kt new file mode 100644 index 00000000000..bb0ff4bb229 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/VariableManager.kt @@ -0,0 +1,48 @@ +package org.kotlinnative.translator + +import org.kotlinnative.translator.llvm.LLVMScope +import org.kotlinnative.translator.llvm.LLVMVariable +import org.kotlinnative.translator.llvm.types.LLVMType +import java.util.* + +class VariableManager(val globalVariableCollection: HashMap) { + + private var fileVariableCollectionTree = HashMap>>() + + private companion object UniqueGenerator { + private var unique = 0 + fun generateUniqueString() = + ".unique." + unique++ + } + + operator fun get(variableName: String): LLVMVariable? { + return fileVariableCollectionTree[variableName]?.peek()?.first ?: globalVariableCollection[variableName] + } + + operator fun contains(variableName: String): Boolean { + return (fileVariableCollectionTree.contains(variableName) && !fileVariableCollectionTree[variableName]!!.empty()) || globalVariableCollection.containsKey(variableName) + } + + fun pullOneUpwardLevelVariable(variableName: String) { + fileVariableCollectionTree[variableName]?.pop() + } + + fun pullUpwardsLevel(level: Int) { + fileVariableCollectionTree.forEach { s, stack -> while (!stack.empty() && stack.peek().second >= level) stack.pop() } + } + + fun addVariable(name: String, variable: LLVMVariable, level: Int) { + val stack = fileVariableCollectionTree.getOrDefault(name, Stack>()) + stack.push(Pair(variable, level)) + fileVariableCollectionTree.put(name, stack) + } + + fun addGlobalVariable(name: String, variable: LLVMVariable) { + globalVariableCollection.put(name, variable) + } + + fun receiveVariable(name: String, type: LLVMType, scope: LLVMScope, pointer: Int): LLVMVariable { + return LLVMVariable("managed${generateUniqueString()}.$name", type, name, scope, pointer) + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/BlockCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/BlockCodegen.kt new file mode 100644 index 00000000000..be04b431c66 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/BlockCodegen.kt @@ -0,0 +1,1121 @@ +package org.kotlinnative.translator.codegens + +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.tree.IElementType +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.* +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl +import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.llvm.* +import org.kotlinnative.translator.llvm.types.* +import java.lang.RuntimeException +import java.lang.UnsupportedOperationException +import java.rmi.UnexpectedException +import kotlin.comparisons.compareBy + + +abstract class BlockCodegen(val state: TranslationState, + val variableManager: VariableManager, + val codeBuilder: LLVMBuilder) { + + val topLevelScopeDepth = 2 + var returnType: LLVMVariable? = null + + fun evaluateCodeBlock(expr: PsiElement?, + startLabel: LLVMLabel? = null, + nextIterationLabel: LLVMLabel? = null, + breakLabel: LLVMLabel? = null, + scopeDepth: Int = 0, + isBlock: Boolean = true) { + codeBuilder.markWithLabel(startLabel) + if (isBlock) { + expressionWalker(expr, breakLabel, scopeDepth) + } else { + var result = evaluateExpression(expr, scopeDepth) + ?: throw UnexpectedException("Can't evaluate expression " + expr!!.text) + when (result) { + is LLVMVariable -> { + if (result.type is LLVMReferenceType) { + generateReferenceReturn(result) + } else { + result = codeBuilder.receivePointedArgument(result, requirePointer = 0) + codeBuilder.addReturnOperator(result) + } + } + else -> codeBuilder.addAnyReturn(result.type, result.toString()) + } + + } + codeBuilder.addUnconditionalJump(nextIterationLabel ?: return) + } + + fun evaluateExpression(expr: PsiElement?, + scopeDepth: Int): LLVMSingleValue? = + when (expr) { + is KtBlockExpression -> { + expressionWalker(expr.lBrace, breakLabel = null, scopeDepth = scopeDepth + 1) + null + } + is KtBinaryExpression -> evaluateBinaryExpression(expr, scopeDepth) + is KtPostfixExpression -> evaluatePostfixExpression(expr, scopeDepth) + is KtPrefixExpression -> evaluatePrefixExpression(expr, scopeDepth) + is KtConstantExpression -> evaluateConstantExpression(expr) + is KtCallExpression -> evaluateCallExpression(expr, scopeDepth) + is KtWhenExpression -> evaluateWhenExpression(expr, scopeDepth) + is KtCallableReferenceExpression -> evaluateCallableReferenceExpression(expr) + is KtDotQualifiedExpression -> evaluateDotExpression(expr, scopeDepth) + is KtReferenceExpression -> evaluateReferenceExpression(expr, scopeDepth) + is KtIfExpression -> evaluateIfOperator(expr, scopeDepth + 1) + is KtStringTemplateExpression -> evaluateStringTemplateExpression(expr) + is KtReturnExpression -> evaluateReturnInstruction(expr.firstChild, scopeDepth) + is KtThisExpression -> evaluateThisExpression() + is KtSafeQualifiedExpression -> evaluateSafeAccessExpression(expr, scopeDepth) + is KtParenthesizedExpression -> evaluateExpression(expr.expression, scopeDepth) + null -> null + is PsiElement -> evaluatePsiElement(expr, scopeDepth) + else -> throw UnsupportedOperationException() + } + + fun evaluateConstructorDelegationReferenceExpression(expr: KtConstructorDelegationReferenceExpression, + structCodegen: StructCodegen, + constructorArguments: List, + scopeDepth: Int): LLVMSingleValue? { + val targetCall = state.bindingContext.get(BindingContext.CALL, expr) + val names = parseValueArguments(targetCall!!.valueArguments, scopeDepth) + val args = codeBuilder.loadArgsIfRequired(names, constructorArguments) + return evaluateConstructorCallExpression( + LLVMVariable(structCodegen.structName + LLVMType.mangleFunctionArguments(names), + structCodegen.type, + scope = LLVMVariableScope()), + args) + } + + fun evaluateMemberMethodOrField(receiver: LLVMVariable, + selectorName: String, + scopeDepth: Int, + call: PsiElement? = null): LLVMSingleValue? { + val type = receiver.type as LLVMReferenceType + val clazz = resolveClassOrObjectLocation(type) + ?: throw UnexpectedException(type.toString() + receiver.toString()) + val field = clazz.fieldsIndex[selectorName] + + if (field != null) { + return evaluateClassField(receiver, field) + } else { + return evaluateMemberMethod(receiver, clazz, scopeDepth, call as? KtCallExpression + ?: throw UnexpectedException("$receiver:$selectorName")) + } + } + + fun addPrimitiveBinaryOperation(operator: IElementType, + firstOp: LLVMSingleValue, + secondOp: LLVMSingleValue, + referenceName: KtSimpleNameExpression? = null): LLVMVariable { + val firstNativeOp = codeBuilder.receiveNativeValue(firstOp) + val secondNativeOp = codeBuilder.receiveNativeValue(secondOp) + + val llvmExpression = when (operator) { + KtTokens.PLUS -> firstOp.type.operatorPlus(firstNativeOp, secondNativeOp) + KtTokens.MINUS -> firstOp.type.operatorMinus(firstNativeOp, secondNativeOp) + KtTokens.MUL -> firstOp.type.operatorTimes(firstNativeOp, secondNativeOp) + KtTokens.DIV -> firstOp.type.operatorDiv(firstNativeOp, secondNativeOp) + KtTokens.LT -> firstOp.type.operatorLt(firstNativeOp, secondNativeOp) + KtTokens.GT -> firstOp.type.operatorGt(firstNativeOp, secondNativeOp) + KtTokens.LTEQ -> firstOp.type.operatorLeq(firstNativeOp, secondNativeOp) + KtTokens.GTEQ -> firstOp.type.operatorGeq(firstNativeOp, secondNativeOp) + KtTokens.EQEQ -> + if (LLVMType.isReferredType(firstOp.type) && LLVMType.isReferredType(secondOp.type)) { + val firstPointedArgument = codeBuilder.receivePointedArgument(firstOp, requirePointer = 1) + val secondPointedArgument = codeBuilder.receivePointedArgument(secondOp, requirePointer = 1) + firstOp.type.operatorEq(firstPointedArgument, secondPointedArgument) + } else + firstOp.type.operatorEq(firstNativeOp, secondNativeOp) + KtTokens.EQEQEQ -> { + val firstPointedArgument = codeBuilder.receivePointedArgument(firstOp, requirePointer = 1) + val secondPointedArgument = codeBuilder.receivePointedArgument(secondOp, requirePointer = 1) + firstOp.type.operatorEq(firstPointedArgument, secondPointedArgument) + } + KtTokens.EXCLEQ -> { + if (LLVMType.isReferredType(firstOp.type) && LLVMType.isReferredType(secondOp.type)) { + val firstPointedArgument = codeBuilder.receivePointedArgument(firstOp, requirePointer = 1) + val secondPointedArgument = codeBuilder.receivePointedArgument(secondOp, requirePointer = 1) + firstOp.type.operatorNeq(firstPointedArgument, secondPointedArgument) + } else + firstOp.type.operatorNeq(firstNativeOp, secondNativeOp) + } + KtTokens.EXCLEQEQEQ -> { + val firstPointedArgument = codeBuilder.receivePointedArgument(firstOp, requirePointer = 1) + val secondPointedArgument = codeBuilder.receivePointedArgument(secondOp, requirePointer = 1) + firstOp.type.operatorNeq(firstPointedArgument, secondPointedArgument) + } + KtTokens.EQ -> { + if (secondOp.type is LLVMNullType) { + codeBuilder.storeNull(firstOp as LLVMVariable) + return firstOp + } + + val result = firstOp as LLVMVariable + val sourceArgument: LLVMSingleValue + if ((firstOp.pointer == 2) && secondOp.type.isPrimitive && (secondOp.pointer == 0)) { + sourceArgument = codeBuilder.getNewVariable(secondOp.type, pointer = 1) + codeBuilder.allocStaticVar(sourceArgument, asValue = true) + codeBuilder.storeVariable(sourceArgument, secondOp) + } else { + sourceArgument = if (result.pointer == secondOp.pointer + 1) secondOp else secondNativeOp + } + + codeBuilder.storeVariable(result, sourceArgument) + return result + } + else -> addPrimitiveReferenceOperationByName(referenceName!!.getReferencedName(), firstOp, secondNativeOp) + } + return codeBuilder.saveExpression(llvmExpression) + } + + private fun expressionWalker(expr: PsiElement?, + breakLabel: LLVMLabel?, + scopeDepth: Int) { + when (expr) { + is KtBlockExpression -> expressionWalker(expr.lBrace, breakLabel, scopeDepth + 1) + is KtProperty -> evaluateValExpression(expr, scopeDepth) + is KtPostfixExpression -> evaluatePostfixExpression(expr, scopeDepth) + is KtBinaryExpression -> evaluateBinaryExpression(expr, scopeDepth) + is KtCallExpression -> evaluateCallExpression(expr, scopeDepth) + is KtDoWhileExpression -> evaluateDoWhileExpression(expr, scopeDepth + 1) + is KtBreakExpression -> evaluateBreakExpression(breakLabel!!) + is KtDotQualifiedExpression -> evaluateDotExpression(expr, scopeDepth) + is KtWhenExpression -> evaluateWhenExpression(expr, scopeDepth) + is PsiElement -> evaluateExpression(expr.firstChild, scopeDepth + 1) + null -> { + variableManager.pullUpwardsLevel(scopeDepth) + return + } + else -> UnsupportedOperationException() + } + + expressionWalker(expr.getNextSiblingIgnoringWhitespaceAndComments(), breakLabel, scopeDepth) + } + + private fun evaluateBreakExpression(breakLabel: LLVMLabel) = + codeBuilder.addUnconditionalJump(breakLabel) + + private fun evaluateDoWhileExpression(expr: KtDoWhileExpression, scopeDepth: Int) = + executeWhileBlock(expr.condition!!, expr.body!!, scopeDepth, checkConditionBeforeExecute = false) + + private fun evaluateThisExpression(): LLVMSingleValue? = + variableManager["this"] + + fun evaluateStringTemplateExpression(expr: KtStringTemplateExpression): LLVMSingleValue? { + val receiveValue = state.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expr) as TypedCompileTimeConstant + val value = receiveValue.getValue(receiveValue.type) ?: return null + val variable = + variableManager.receiveVariable(".str", + LLVMStringType(value.toString().length, isLoaded = false), + LLVMVariableScope(), + pointer = 0) + + codeBuilder.addStringConstant(variable, value.toString()) + return variable + } + + private fun evaluateCallableReferenceExpression(expr: KtCallableReferenceExpression): LLVMSingleValue? { + val kotlinType = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, expr)!!.type!! + val result = LLVMInstanceOfStandardType(expr.callableReference.text, kotlinType, LLVMVariableScope(), state) + return LLVMVariable(result.label + (result.type as LLVMFunctionType).mangleArgs(), + result.type, + result.kotlinName, + result.scope, + result.pointer) + } + + private fun evaluateSafeAccessExpression(expr: KtSafeQualifiedExpression, + scopeDepth: Int): LLVMSingleValue? { + val receiver = expr.receiverExpression + val selector = expr.selectorExpression + + val left = evaluateExpression(receiver, scopeDepth)!! + val loadedLeft = codeBuilder.receiveNativeValue(left) + val expectedType = LLVMMapStandardType(state.bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expr)!!, state) as LLVMReferenceType + + val result = codeBuilder.getNewVariable(expectedType, pointer = 2) + codeBuilder.allocStaticVar(result, pointer = true) + + val condition = left.type.operatorEq(loadedLeft, LLVMVariable("", LLVMNullType())) + val nullLabel = codeBuilder.getNewLabel(prefix = "safe.access") + val notNullLabel = codeBuilder.getNewLabel(prefix = "safe.access") + val endLabel = codeBuilder.getNewLabel(prefix = "safe.access") + + val conditionResult = codeBuilder.saveExpression(condition) + codeBuilder.addCondition(conditionResult, nullLabel, notNullLabel) + + codeBuilder.markWithLabel(nullLabel) + codeBuilder.storeNull(result) + codeBuilder.addUnconditionalJump(endLabel) + + codeBuilder.markWithLabel(notNullLabel) + val right = evaluateDotBody(receiver, selector!!, scopeDepth) as LLVMVariable + val rightLoaded = codeBuilder.loadAndGetVariable(right) + codeBuilder.storeVariable(result, rightLoaded) + codeBuilder.addUnconditionalJump(endLabel) + + codeBuilder.markWithLabel(endLabel) + + return result + } + + private fun evaluateDotExpression(expr: KtDotQualifiedExpression, + scopeDepth: Int): LLVMSingleValue? { + val receiverExpr = expr.receiverExpression + val selectorExpr = expr.selectorExpression!! + + return evaluateDotBody(receiverExpr, selectorExpr, scopeDepth) + } + + private fun evaluateDotBody(receiverExpr: KtExpression, + selectorExpr: KtExpression, + scopeDepth: Int): LLVMSingleValue? { + var receiverName = receiverExpr.text + var receiver = when (receiverExpr) { + is KtCallExpression, + is KtPrefixExpression, + is KtPostfixExpression, + is KtBinaryExpression -> evaluateExpression(receiverExpr, scopeDepth) as LLVMVariable + is KtDotQualifiedExpression -> { + val codegen = resolveCodegenByName(receiverName) + if (codegen != null) null else evaluateExpression(receiverExpr, scopeDepth) as LLVMVariable + } + is KtNameReferenceExpression -> { + val referenceContext = state.bindingContext.get(BindingContext.REFERENCE_TARGET, receiverExpr) + receiverName = referenceContext!!.fqNameSafe.asString() + when (referenceContext) { + is PropertyDescriptorImpl -> { + val receiverThis = variableManager["this"]!! + evaluateMemberMethodOrField(receiverThis, receiverName, topLevelScopeDepth, call = null)!! as LLVMVariable + } + else -> variableManager[receiverName] + } + } + else -> variableManager[receiverName] + } + + val isExtension = selectorExpr is KtCallExpression && + selectorExpr.getFunctionResolvedCallWithAssert(state.bindingContext).extensionReceiver != null + + if (isExtension) { + return evaluateExtensionExpression(receiverExpr, receiver, selectorExpr as KtCallExpression, scopeDepth) + } + + val selectorName = when (selectorExpr) { + is KtNameReferenceExpression -> state.bindingContext.get(BindingContext.REFERENCE_TARGET, selectorExpr) + ?.fqNameSafe?.asString() ?: selectorExpr.text + else -> selectorExpr.text + } + + if (receiver != null) { + if (receiver.pointer == 2) { + receiver = codeBuilder.loadAndGetVariable(receiver) + } + when (receiver.type) { + is LLVMReferenceType -> return evaluateMemberMethodOrField(receiver, selectorName, scopeDepth, selectorExpr) + else -> return evaluateExtensionExpression(receiverExpr, receiver, selectorExpr as KtCallExpression, scopeDepth) + } + } + + val clazz = resolveCodegen(receiverExpr) + ?: return evaluateExtensionExpression(receiverExpr, receiver, selectorExpr as? KtCallExpression + ?: throw UnexpectedException("Failed evaluate dot expression: " + selectorExpr.text), scopeDepth) + return evaluateClassScopedDotExpression(clazz, selectorExpr, scopeDepth, receiver) + } + + private fun evaluateExtensionExpression(receiver: KtExpression, + receiverExpressionArgument: LLVMVariable?, + selector: KtCallExpression, + scopeDepth: Int): LLVMSingleValue? { + val kotlinType = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, receiver)!!.type!! + val receiverType = LLVMMapStandardType(kotlinType, state) + + val targetFunction = state.bindingContext.get(BindingContext.CALL, selector.calleeExpression) + val candidateDescriptor = state.bindingContext.get(BindingContext.RESOLVED_CALL, targetFunction)!!.candidateDescriptor + val targetFunctionName = candidateDescriptor.fqNameSafe.convertToNativeName() + val nameWithoutMangling = candidateDescriptor.name.asString().replace(Regex("""(.?)"""), "") + val packageNameFirst = targetFunction?.calleeExpression?.getContainingKtFile()?.packageFqName?.convertToNativeName().orEmpty() + val packageNameSecond = candidateDescriptor.containingDeclaration.fqNameSafe.convertToNativeName() + + val names = parseArgList(selector, scopeDepth) + val type = LLVMType.mangleFunctionArguments(names) + + val constructedFunctionName = receiverType.mangle + nameWithoutMangling.addBeforeIfNotEmpty(".") + type + val targetExtension = state.extensionFunctions[receiverType.toString()] + + val extensionCodegen = targetExtension?.get(packageNameFirst.addAfterIfNotEmpty(".") + constructedFunctionName) ?: + targetExtension?.get(packageNameSecond.addAfterIfNotEmpty(".") + constructedFunctionName) ?: + targetExtension?.get(targetFunctionName) + ?: throw UnexpectedException(constructedFunctionName) + val receiverExpression = receiverExpressionArgument ?: evaluateExpression(receiver, scopeDepth + 1)!! + + val args = mutableListOf(codeBuilder.receivePointedArgument(receiverExpression, requirePointer = if (receiverType is LLVMReferenceType) 1 else 0)) + args.addAll(codeBuilder.loadArgsIfRequired(names, extensionCodegen.args)) + return evaluateFunctionCallExpression(LLVMVariable(extensionCodegen.fullName, extensionCodegen.returnType!!.type, scope = LLVMVariableScope()), args) + } + + private fun evaluateClassScopedDotExpression(clazz: StructCodegen, + selector: KtExpression, + scopeDepth: Int, + receiver: LLVMVariable? = null): LLVMSingleValue? = + when (selector) { + is KtCallExpression -> evaluateCallExpression(selector, scopeDepth, clazz, caller = receiver) + is KtReferenceExpression -> evaluateReferenceExpression(selector, scopeDepth, clazz) + else -> throw UnsupportedOperationException() + } + + private fun evaluateClassField(receiver: LLVMVariable, + field: LLVMClassVariable): LLVMVariable { + val result = codeBuilder.getNewVariable(field.type, pointer = field.pointer + 1) + codeBuilder.loadClassField(result, receiver, field.offset) + return result + } + + private fun evaluateMemberMethod(receiver: LLVMVariable, + clazz: StructCodegen, + scopeDepth: Int, + call: KtCallExpression): LLVMSingleValue? { + val resolvedCall = call.getCall(state.bindingContext)!!.getResolvedCallWithAssert(state.bindingContext) + val functionDescriptor = resolvedCall.candidateDescriptor + val functionArguments = functionDescriptor.valueParameters.map { it -> it.type }.map { LLVMMapStandardType(it, state) } + val methodName = functionDescriptor.fqNameSafe.asString() + LLVMType.mangleFunctionTypes(functionArguments) + + val method = clazz.methods[methodName] ?: throw UnexpectedException(methodName) + val returnType = method.returnType!!.type + + val arguments = resolvedCall.valueArguments.toSortedMap(compareBy { it.index }).values + val substitutionArguments = parseArgumentsWithDefaultValues(arguments, method.defaultValues, scopeDepth) + val loadedArgs = codeBuilder.loadArgsIfRequired(substitutionArguments, method.args) + + val callArgs = mutableListOf(receiver) + callArgs.addAll(loadedArgs) + + return evaluateFunctionCallExpression(LLVMVariable(methodName, returnType, scope = LLVMVariableScope()), callArgs) + } + + private fun resolveClassOrObjectLocation(type: LLVMReferenceType): StructCodegen? { + val typeLocations = type.type.split('.') + val currentLocation = StringBuilder() + var codegen: ClassCodegen? = null + var currentIndex = 0 + + do { + currentLocation.append((if (currentIndex > 0) "." else "") + typeLocations[currentIndex]) + if (state.classes.containsKey(currentLocation.toString())) { + codegen = state.classes[currentLocation.toString()] + } else if (state.objects.containsKey(currentLocation.toString())) { + return state.objects[currentLocation.toString()] + } + currentIndex++ + } while ((currentIndex < typeLocations.size) && (codegen == null)) + + while (currentIndex < typeLocations.size - 1) { + currentLocation.append('.' + typeLocations[currentIndex]) + currentIndex++ + codegen = codegen?.nestedClasses?.get(currentLocation.toString()) + } + + if (codegen?.companionObjectCodegen != null && type.type == codegen?.companionObjectCodegen?.structName) { + return codegen?.companionObjectCodegen!! + } + + return if (codegen?.structName == type.type) codegen else codegen?.nestedClasses?.get(type.type) + } + + private fun evaluateArrayAccessExpression(expr: KtArrayAccessExpression, + scope: Int): LLVMSingleValue? { + val arrayNameVariable = evaluateExpression(expr.arrayExpression, scope) as LLVMVariable + return when (arrayNameVariable.type) { + is LLVMReferenceType -> { + val callMaker = state.bindingContext.get(BindingContext.CALL, expr) + when (callMaker!!.callType) { + Call.CallType.ARRAY_SET_METHOD, + Call.CallType.ARRAY_GET_METHOD -> { + val arrayActionType = if (callMaker.callType == Call.CallType.ARRAY_SET_METHOD) "set" else "get" + val explicitReceiver = callMaker.explicitReceiver as ExpressionReceiver + val receiver = evaluateExpression(explicitReceiver.expression, scope)!! as LLVMVariable + val pureReceiver = codeBuilder.receivePointedArgument(receiver, requirePointer = 1) + + val targetClassName = (receiver.type as LLVMReferenceType).type + + val names = parseValueArguments(callMaker.valueArguments, scope) + val methodName = "$targetClassName.$arrayActionType${LLVMType.mangleFunctionArguments(names)}" + val clazz = resolveClassOrObjectLocation(receiver.type) + ?: throw UnexpectedException(receiver.type.toString()) + + val method = clazz.methods[methodName] ?: throw UnexpectedException(expr.text) + val returnType = method.returnType!!.type + + val loadedArgs = codeBuilder.loadArgsIfRequired(names, method.args) + val callArgs = mutableListOf(pureReceiver) + callArgs.addAll(loadedArgs) + + return evaluateFunctionCallExpression(LLVMVariable(methodName, returnType, scope = LLVMVariableScope()), callArgs) + } + else -> throw UnexpectedException("Unknown array access method") + } + } + else -> { + val arrayIndex = evaluateConstantExpression(expr.indexExpressions.first() as KtConstantExpression) + val arrayReceivedVariable = codeBuilder.loadAndGetVariable(arrayNameVariable) + val arrayElementType = (arrayNameVariable.type as LLVMArray).arrayElementType + val arrayElement = codeBuilder.getNewVariable(arrayElementType, pointer = 1) + codeBuilder.loadVariableOffset(arrayElement, arrayReceivedVariable, arrayIndex) + arrayElement + } + } + } + + private fun evaluateReferenceExpression(expr: KtReferenceExpression, + scopeDepth: Int, + classScope: StructCodegen? = null): LLVMSingleValue? { + val targetName = state.bindingContext.get(BindingContext.REFERENCE_TARGET, expr)?.fqNameSafe?.convertToNativeName() + return when { + expr is KtArrayAccessExpression -> evaluateArrayAccessExpression(expr, scopeDepth + 1) + isEnumClassField(expr, classScope) -> resolveEnumClassField(expr, classScope) + (targetName != null) && variableManager.contains(targetName) -> variableManager[targetName] + ((expr is KtNameReferenceExpression) && (classScope != null)) -> + evaluateNameReferenceExpression(targetName!!, classScope.parentCodegen!! as ClassCodegen) + else -> { + val clazz = classScope ?: resolveCodegen(expr) + val receiver = if (clazz != null) variableManager[clazz.structName] ?: variableManager["this"] else variableManager["this"] + targetName ?: throw UnexpectedException("Can't find target in reference expression " + expr.firstChild.text) + evaluateMemberMethodOrField(receiver ?: throw UnexpectedException(targetName), targetName, topLevelScopeDepth) + } + } + } + + private fun evaluateNameReferenceExpression(fieldName: String, + classScope: ClassCodegen): LLVMSingleValue? { + val companionObject = classScope.companionObjectCodegen!! + val field = companionObject.fieldsIndex[fieldName] ?: return null + val receiver = variableManager[companionObject.structName]!! + val result = codeBuilder.getNewVariable(field.type, pointer = 1) + + codeBuilder.loadClassField(result, receiver, field.offset) + return result + } + + private fun resolveEnumClassField(expr: KtReferenceExpression, + classScope: StructCodegen?): LLVMSingleValue = + (classScope ?: resolveCodegen(expr))!!.enumFields[expr.text]!! + + private fun isEnumClassField(expr: KtReferenceExpression, + classScope: StructCodegen?): Boolean = + (classScope ?: resolveCodegen(expr))?.enumFields?.containsKey(expr.text) ?: false + + private fun resolveCodegen(expr: KtExpression): StructCodegen? { + val type = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, expr)?.type + ?: expr.getQualifiedExpressionForReceiver()?.getType(state.bindingContext) + + val name = type?.constructor?.declarationDescriptor?.fqNameSafe?.asString() ?: throw UnexpectedException(expr.text) + + return resolveCodegenByName(name) + } + + private fun resolveCodegenByName(name: String): StructCodegen? = + resolveClassOrObjectLocation(LLVMReferenceType(name, prefix = "class")) + + private fun evaluateCallExpression(expr: KtCallExpression, + scopeDepth: Int, + classScope: StructCodegen? = null, + caller: LLVMVariable? = null): LLVMSingleValue? { + var names = parseArgList(expr, scopeDepth) + val targetFunction = state.bindingContext.get(BindingContext.CALL, expr.calleeExpression) + var resolvedCall = state.bindingContext.get(BindingContext.RESOLVED_CALL, targetFunction) + if (resolvedCall is VariableAsFunctionResolvedCallImpl) { + resolvedCall = resolvedCall.variableCall + } + + val functionDescriptor = resolvedCall!!.candidateDescriptor + val targetFunctionName = functionDescriptor.fqNameSafe.convertToNativeName() + val externalFunctionName = functionDescriptor.name.asString() + val arguments = resolvedCall.valueArguments.toSortedMap(compareBy { it.index }).values + + val external = state.externalFunctions.containsKey(externalFunctionName) + val functionArguments = functionDescriptor.valueParameters.map { it -> it.type }.map { LLVMMapStandardType(it, state) } + val function = "$targetFunctionName${if (!external) LLVMType.mangleFunctionTypes(functionArguments) else ""}" + + if (function in state.functions || externalFunctionName in state.externalFunctions) { + val descriptor = state.functions[function] ?: state.externalFunctions[externalFunctionName]!! + names = parseArgumentsWithDefaultValues(arguments, descriptor.defaultValues, scopeDepth) + val args = codeBuilder.loadArgsIfRequired(names, descriptor.args) + return evaluateFunctionCallExpression(LLVMVariable(descriptor.name, descriptor.returnType!!.type, scope = LLVMVariableScope()), args) + } + + if (targetFunctionName in state.classes || classScope?.structName == targetFunctionName) { + val descriptor = state.classes[targetFunctionName] ?: classScope ?: return null + val detectedConstructor = LLVMType.mangleFunctionTypes(functionArguments) + val args = codeBuilder.loadArgsIfRequired(names, descriptor.constructorFields[detectedConstructor]!!) + return evaluateConstructorCallExpression(LLVMVariable(descriptor.structName + detectedConstructor, descriptor.type, scope = LLVMVariableScope()), args) + } + + if (targetFunctionName in variableManager) { + val type = variableManager[targetFunctionName]!!.type as LLVMFunctionType + val args = codeBuilder.loadArgsIfRequired(names, type.arguments) + return evaluateFunctionCallExpression(LLVMVariable(targetFunctionName, type.returnType.type, scope = LLVMRegisterScope()), args) + } + + val nestedConstructor = classScope?.nestedClasses?.get(expr.calleeExpression!!.text) + if (nestedConstructor != null) { + val args = codeBuilder.loadArgsIfRequired(names, nestedConstructor.constructorFields[nestedConstructor.primaryConstructorIndex]!!) + return evaluateConstructorCallExpression(LLVMVariable(nestedConstructor.structName, nestedConstructor.type, scope = LLVMVariableScope()), args) + } + + val containingClass = resolveContainingClass(expr) ?: return null + val method = containingClass.methods[function] ?: throw RuntimeException("Cannot find function $targetFunctionName") + val args = mutableListOf() + + if (caller != null) { + args.add(caller) + } else if (containingClass.structName in variableManager) { + args.add(variableManager[containingClass.structName]!!) + } else { + args.add(variableManager["this"]!!) + } + + args.addAll(codeBuilder.loadArgsIfRequired(names, method.args)) + + return evaluateFunctionCallExpression(LLVMVariable(method.fullName, method.returnType?.type ?: LLVMVoidType(), scope = LLVMVariableScope()), args) + } + + private fun resolveContainingClass(expr: KtElement): StructCodegen? { + val name = expr.getResolvedCallWithAssert(state.bindingContext).dispatchReceiver?.type?.constructor?.declarationDescriptor?.fqNameSafe?.asString() ?: return null + return resolveCodegenByName(name) + } + + private fun evaluateFunctionCallExpression(function: LLVMVariable, + names: List): LLVMSingleValue? { + val returnType = function.type + when (returnType) { + is LLVMVoidType -> { + codeBuilder.addLLVMCodeToLocalPlace(LLVMCall(LLVMVoidType(), function.toString(), names).toString()) + return null + } + is LLVMReferenceType -> { + val returnVar = codeBuilder.getNewVariable(returnType, pointer = 2) + codeBuilder.allocStaticVar(returnVar, pointer = true) + + val args = mutableListOf(returnVar) + args.addAll(names) + + codeBuilder.addLLVMCodeToLocalPlace(LLVMCall(LLVMVoidType(), function.toString(), args).toString()) + if (returnVar.pointer == 2) { + return returnVar + } else { + return codeBuilder.loadAndGetVariable(returnVar) + } + } + else -> { + val result = codeBuilder.saveExpression(LLVMCall(returnType, function.toString(), names)) + + val resultPtr = codeBuilder.getNewVariable(returnType, pointer = 1) + codeBuilder.allocStackVar(resultPtr, pointer = true) + codeBuilder.storeVariable(resultPtr, result) + return resultPtr + } + } + } + + private fun evaluateConstructorCallExpression(function: LLVMVariable, + names: List): LLVMSingleValue? { + val store = codeBuilder.getNewVariable(function.type, pointer = 1) + codeBuilder.allocStaticVar(store, pointer = true) + + val result = codeBuilder.getNewVariable(function.type, pointer = 2) + codeBuilder.allocStackVar(result, pointer = true) + codeBuilder.storeVariable(result, store) + + val args = mutableListOf(store) + args.addAll(names) + + codeBuilder.addLLVMCodeToLocalPlace(LLVMCall( + LLVMVoidType(), + function.toString(), + args + ).toString()) + + return result + } + + private fun parseArgList(expr: KtCallExpression, + scopeDepth: Int): List = + parseValueArguments(expr.getValueArgumentsInParentheses(), scopeDepth) + + private fun parseValueArguments(args: List, scopeDepth: Int): List = + args.map { evaluateExpression(it.getArgumentExpression(), scopeDepth) as LLVMSingleValue } + + private fun parseArgumentsWithDefaultValues(args: MutableCollection, defaultValues: List, scopeDepth: Int): List = + args.mapIndexed(fun(i: Int, value: ResolvedValueArgument): LLVMSingleValue { + return when (value) { + is DefaultValueArgument -> evaluateExpression(defaultValues[i], scopeDepth)!! + is ExpressionValueArgument -> evaluateExpression((value.valueArgument as KtValueArgument).getArgumentExpression()!!, scopeDepth)!! + else -> throw UnexpectedException("Wrong parser argument") + } + }).toList() + + private fun evaluateBinaryExpression(expr: KtBinaryExpression, + scopeDepth: Int): LLVMVariable? { + val operator = expr.operationToken + + if (operator == KtTokens.ELVIS) { + return evaluateElvisOperator(expr, scopeDepth) + } + + val left = evaluateExpression(expr.left, scopeDepth) + if (expr.left is KtArrayAccessExpression) { + val callMaker = state.bindingContext.get(BindingContext.CALL, expr.left) + if (callMaker!!.callType == Call.CallType.ARRAY_SET_METHOD) { + return left as LLVMVariable? + } + } + + left ?: throw UnsupportedOperationException("Wrong binary expression: ${expr.text}") + val right = evaluateExpression(expr.right, scopeDepth) + ?: throw UnsupportedOperationException("Wrong binary expression: ${expr.text}") + + return addPrimitiveBinaryOperation(operator, left, right, expr.operationReference) + } + + private fun evaluatePostfixExpression(expr: KtPostfixExpression, + scopeDepth: Int): LLVMSingleValue? { + val operator = expr.operationToken + val left = evaluateExpression(expr.baseExpression, scopeDepth) + ?: throw UnsupportedOperationException("Wrong binary expression: ${expr.text}") + return addPrimitivePostfixOperation(operator, left as LLVMVariable) + } + + private fun evaluatePrefixExpression(expr: KtPrefixExpression, + scopeDepth: Int): LLVMSingleValue? { + val operator = expr.operationToken + val left = evaluateExpression(expr.baseExpression, scopeDepth) + ?: throw UnsupportedOperationException("Wrong binary expression") + return addPrimitivePrefixOperation(operator, left) + } + + private fun addPrimitivePostfixOperation(operator: IElementType?, + firstOp: LLVMVariable): LLVMSingleValue? = + when (operator) { + KtTokens.PLUSPLUS, KtTokens.MINUSMINUS -> { + val firstNativeOp = codeBuilder.receiveNativeValue(firstOp) + val oldValue = codeBuilder.getNewVariable(firstOp.type, firstOp.pointer) + codeBuilder.allocStackVar(oldValue, asValue = true) + codeBuilder.copyVariable(firstOp, oldValue) + + val llvmExpression = when (operator) { + KtTokens.PLUSPLUS -> firstOp.type.operatorInc(firstNativeOp) + KtTokens.MINUSMINUS -> firstOp.type.operatorDec(firstNativeOp) + else -> throw IllegalAccessError() + } + + val resultOp = codeBuilder.saveExpression(llvmExpression) + codeBuilder.storeVariable(firstOp, resultOp) + oldValue + } + KtTokens.EXCLEXCL -> { + var result = firstOp + val nullLabel = codeBuilder.getNewLabel(prefix = "nullCheck") + val notNullLabel = codeBuilder.getNewLabel(prefix = "nullCheck") + val nullCheck = codeBuilder.nullCheck(firstOp) + codeBuilder.addCondition(nullCheck, nullLabel, notNullLabel) + codeBuilder.markWithLabel(nullLabel) + codeBuilder.addExceptionCall("KotlinNullPointerException") + codeBuilder.addUnconditionalJump(notNullLabel) + codeBuilder.markWithLabel(notNullLabel) + if (firstOp.type.isPrimitive) { + result = codeBuilder.receivePointedArgument(firstOp, requirePointer = 0) as LLVMVariable + } + result + } + else -> throw UnsupportedOperationException() + } + + + private fun addPrimitivePrefixOperation(operator: IElementType?, + firstOp: LLVMSingleValue): LLVMSingleValue? = + when (operator) { + KtTokens.MINUS, + KtTokens.PLUS -> addPrimitiveBinaryOperation(operator!!, LLVMConstant("0", firstOp.type), firstOp) + KtTokens.EXCL -> { + val firstNativeOp = codeBuilder.receiveNativeValue(firstOp) + val llvmExpression = addPrimitiveReferenceOperationByName("xor", LLVMConstant("true", LLVMBooleanType()), firstNativeOp) + codeBuilder.saveExpression(llvmExpression) + } + else -> throw UnsupportedOperationException() + } + + + private fun evaluateElvisOperator(expr: KtBinaryExpression, + scopeDepth: Int): LLVMVariable { + val left = evaluateExpression(expr.left, scopeDepth) + ?: throw UnsupportedOperationException("Wrong binary expression") + val lptr = codeBuilder.loadAndGetVariable(left as LLVMVariable) + + val condition = lptr.type.operatorEq(lptr, LLVMVariable("", LLVMNullType())) + + val conditionResult = codeBuilder.saveExpression(condition) + + val notNull = codeBuilder.getNewLabel(prefix = "elvis") + val endLabel = codeBuilder.getNewLabel(prefix = "elvis") + + codeBuilder.addCondition(conditionResult, notNull, endLabel) + + codeBuilder.markWithLabel(notNull) + var right = evaluateExpression(expr.right, scopeDepth + 1) + if (right != null) { + right = codeBuilder.loadAndGetVariable(right as LLVMVariable) + codeBuilder.storeVariable(left, right) + } + + codeBuilder.addUnconditionalJump(endLabel) + codeBuilder.markWithLabel(endLabel) + + return left + } + + private fun addPrimitiveReferenceOperationByName(operator: String, + firstOp: LLVMSingleValue, + secondNativeOp: LLVMSingleValue): LLVMExpression { + val firstNativeOp = codeBuilder.receiveNativeValue(firstOp) + + return when (operator) { + "||", + "or" -> firstNativeOp.type.operatorOr(firstNativeOp, secondNativeOp) + "xor" -> firstNativeOp.type.operatorXor(firstNativeOp, secondNativeOp) + "&&", + "and" -> firstNativeOp.type.operatorAnd(firstNativeOp, secondNativeOp) + "%" -> firstNativeOp.type.operatorMod(firstNativeOp, secondNativeOp) + "shl" -> firstNativeOp.type.operatorShl(firstNativeOp, codeBuilder.convertVariableToType(secondNativeOp, firstNativeOp.type)) + "shr" -> firstNativeOp.type.operatorShr(firstNativeOp, codeBuilder.convertVariableToType(secondNativeOp, firstNativeOp.type)) + "ushr" -> firstNativeOp.type.operatorUshr(firstNativeOp, codeBuilder.convertVariableToType(secondNativeOp, firstNativeOp.type)) + "+=" -> { + val resultOp = codeBuilder.saveExpression(firstNativeOp.type.operatorPlus(firstNativeOp, secondNativeOp)) + codeBuilder.storeVariable(firstOp, resultOp) + return LLVMExpression(resultOp.type, "load ${firstOp.pointedType} $firstOp, align ${firstOp.type.align}") + } + "-=" -> { + val resultOp = codeBuilder.saveExpression(firstNativeOp.type.operatorMinus(firstNativeOp, secondNativeOp)) + codeBuilder.storeVariable(firstOp, resultOp) + return LLVMExpression(resultOp.type, "load ${firstOp.pointedType} $firstOp, align ${firstOp.type.align}") + } + "*=" -> { + val resultOp = codeBuilder.saveExpression(firstNativeOp.type.operatorTimes(firstNativeOp, secondNativeOp)) + codeBuilder.storeVariable(firstOp, resultOp) + return LLVMExpression(resultOp.type, "load ${firstOp.pointedType} $firstOp, align ${firstOp.type.align}") + } + "%=" -> { + val resultOp = codeBuilder.saveExpression(firstNativeOp.type.operatorMod(firstNativeOp, secondNativeOp)) + codeBuilder.storeVariable(firstOp, resultOp) + return LLVMExpression(resultOp.type, "load ${firstOp.pointedType} $firstOp, align ${firstOp.type.align}") + } + ".." -> { + val descriptor = state.classes["kotlin.ranges.${firstOp.type.mangle}Range"] + val arguments = listOf(firstOp, secondNativeOp) + val detectedConstructor = LLVMType.mangleFunctionTypes(arguments.map { it.type }) + val result = evaluateConstructorCallExpression(LLVMVariable(descriptor!!.structName + detectedConstructor, descriptor.type, scope = LLVMVariableScope()), arguments) + return LLVMExpression(result!!.type, "load ${descriptor.type}** $result, align ${descriptor.type.align}", pointer = 1) + } + else -> throw UnsupportedOperationException("Unknown binary operator: $operator(${firstNativeOp.type}, ${secondNativeOp.type})") + } + } + + private fun evaluateConstantExpression(expr: KtConstantExpression): LLVMConstant { + val expressionKotlinType = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, expr)!!.type!! + val expressionValue = state.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expr)?.getValue(expressionKotlinType) + val type = LLVMMapStandardType(expressionKotlinType, state) + return LLVMConstant(expressionValue?.toString().orEmpty(), type, pointer = 0) + } + + private fun evaluatePsiElement(element: PsiElement, + scopeDepth: Int): LLVMSingleValue? = + when (element) { + is LeafPsiElement -> evaluateLeafPsiElement(element, scopeDepth) + is KtConstantExpression -> evaluateConstantExpression(element) + else -> null + } + + + private fun evaluateLeafPsiElement(element: LeafPsiElement, + scopeDepth: Int): LLVMVariable? { + return when (element.elementType) { + KtTokens.RETURN_KEYWORD -> evaluateReturnInstruction(element, scopeDepth) + KtTokens.IF_KEYWORD -> evaluateIfOperator(element.context as KtIfExpression, scopeDepth, isExpression = false) + KtTokens.WHILE_KEYWORD -> evaluateWhileOperator(element.context as KtWhileExpression, scopeDepth) + KtTokens.FOR_KEYWORD -> evaluateForOperator(element.context as KtForExpression, scopeDepth) + else -> null + } + } + + private fun evaluateForOperator(expr: KtForExpression, + scopeDepth: Int): LLVMVariable? { + val range = evaluateExpression(expr.loopRange, scopeDepth + 1)!! + val conditionLabel = codeBuilder.getNewLabel(prefix = "for_condition") + val bodyLabel = codeBuilder.getNewLabel(prefix = "for_body") + val exitLabel = codeBuilder.getNewLabel(prefix = "for_exit") + val rangeTypeName = (range.type as LLVMReferenceType).type + + val descriptor = state.classes[rangeTypeName] ?: throw UnexpectedException("Error occurred in evaluating range expression") + val method = descriptor.methods["$rangeTypeName.iterator"] ?: throw UnexpectedException("Cant receive iterator $rangeTypeName.iterator") + val returnType = method.returnType!!.type + val returnTypeName = (returnType as LLVMReferenceType).type + val iteratorDescriptor = state.classes[returnTypeName] + val nextDescriptor = iteratorDescriptor!!.methods["$returnTypeName.next"] ?: throw UnexpectedException("$returnTypeName.nextInt") + + val conditionIterator = evaluateFunctionCallExpression(LLVMVariable("$rangeTypeName.iterator", returnType, scope = LLVMVariableScope()), listOf(range))!! + val iteratorThisArgument = codeBuilder.receivePointedArgument(conditionIterator, requirePointer = 1) + codeBuilder.addUnconditionalJump(conditionLabel) + codeBuilder.markWithLabel(conditionLabel) + var conditionResult = evaluateFunctionCallExpression(LLVMVariable("$returnTypeName.hasNext", LLVMBooleanType(), scope = LLVMVariableScope()), listOf(iteratorThisArgument))!! + conditionResult = codeBuilder.receivePointedArgument(conditionResult, requirePointer = 0) + codeBuilder.addCondition(conditionResult, bodyLabel, exitLabel) + + codeBuilder.addUnconditionalJump(bodyLabel) + codeBuilder.markWithLabel(bodyLabel) + val loopParameter = evaluateFunctionCallExpression(LLVMVariable("$returnTypeName.next", LLVMIntType(), scope = LLVMVariableScope()), listOf(iteratorThisArgument))!! + val loopParameterDescriptor = state.bindingContext.get(BindingContext.VALUE_PARAMETER, expr.loopParameter!!)?.fqNameSafe?.asString() ?: expr.loopParameter!!.name!! + + val allocVar = variableManager.receiveVariable(loopParameterDescriptor, nextDescriptor.returnType!!.type, LLVMRegisterScope(), pointer = + nextDescriptor.returnType!!.pointer + 1) + variableManager.addVariable(loopParameterDescriptor, allocVar, scopeDepth + 1) + codeBuilder.allocStackVar(allocVar, pointer = true) + + addPrimitiveBinaryOperation(KtTokens.EQ, allocVar, loopParameter, null) + + evaluateCodeBlock(expr.body, startLabel = null, nextIterationLabel = conditionLabel, breakLabel = exitLabel, scopeDepth = scopeDepth + 1) + codeBuilder.markWithLabel(exitLabel) + + return null + } + + private fun evaluateWhenItem(item: KtWhenEntry, + target: LLVMSingleValue, + resultVariable: LLVMVariable, + elseLabel: LLVMLabel, + endLabel: LLVMLabel, + isElse: Boolean, + scopeDepth: Int) { + val successConditionsLabel = codeBuilder.getNewLabel(prefix = "when_condition_success") + var nextLabel = codeBuilder.getNewLabel(prefix = "when_condition_condition") + + codeBuilder.addUnconditionalJump(nextLabel) + + for (condition in item.conditions) { + codeBuilder.markWithLabel(nextLabel) + nextLabel = codeBuilder.getNewLabel(prefix = "when_condition_condition") + + val currentConditionExpression = + evaluateExpression((condition as KtWhenConditionWithExpression).expression, scopeDepth + 1)!! + val conditionResult = addPrimitiveBinaryOperation(KtTokens.EQEQ, target, currentConditionExpression) + + codeBuilder.addCondition(conditionResult, successConditionsLabel, nextLabel) + } + + codeBuilder.markWithLabel(nextLabel) + codeBuilder.addUnconditionalJump(if (isElse) successConditionsLabel else elseLabel) + codeBuilder.markWithLabel(successConditionsLabel) + + var successExpression = evaluateExpression(item.expression, scopeDepth + 1) + + if (successExpression != null && !LLVMType.nullOrVoidType(resultVariable.type)) { + successExpression = codeBuilder.receivePointedArgument(successExpression, requirePointer = 0) + codeBuilder.storeVariable(resultVariable, successExpression) + } + + codeBuilder.addUnconditionalJump(endLabel) + } + + private fun evaluateWhenExpression(expr: KtWhenExpression, + scopeDepth: Int): LLVMVariable? { + codeBuilder.addComment("start when expression") + val whenExpression = expr.subjectExpression + val kotlinType = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, expr)!!.type!! + val expressionType = LLVMMapStandardType(kotlinType, state) + + val targetExpression = evaluateExpression(whenExpression, scopeDepth + 1) ?: LLVMConstant("true", LLVMBooleanType()) + val resultVariable = codeBuilder.getNewVariable(expressionType, pointer = 1) + + when (expressionType) { + is LLVMVoidType, + is LLVMNullType -> { + } + is LLVMReferenceType -> codeBuilder.allocStaticVar(resultVariable, asValue = true) + else -> codeBuilder.allocStackVar(resultVariable, asValue = true) + } + + var nextLabel = codeBuilder.getNewLabel(prefix = "when_start") + val endLabel = codeBuilder.getNewLabel(prefix = "when_end") + codeBuilder.addUnconditionalJump(nextLabel) + for (item in expr.entries) { + codeBuilder.markWithLabel(nextLabel) + nextLabel = codeBuilder.getNewLabel(prefix = "when_item") + evaluateWhenItem(item, targetExpression, resultVariable, nextLabel, endLabel, item.isElse, scopeDepth + 1) + } + + codeBuilder.markWithLabel(nextLabel) + codeBuilder.addUnconditionalJump(endLabel) + codeBuilder.markWithLabel(endLabel) + return resultVariable + } + + private fun evaluateWhileOperator(expr: KtWhileExpression, + scopeDepth: Int): LLVMVariable? = + executeWhileBlock(expr.condition!!, expr.body!!, scopeDepth, checkConditionBeforeExecute = true) + + private fun executeWhileBlock(condition: KtExpression, bodyExpression: PsiElement, scopeDepth: Int, checkConditionBeforeExecute: Boolean): LLVMVariable? { + val conditionLabel = codeBuilder.getNewLabel(prefix = "while") + val bodyLabel = codeBuilder.getNewLabel(prefix = "while") + val exitLabel = codeBuilder.getNewLabel(prefix = "while") + + codeBuilder.addUnconditionalJump(if (checkConditionBeforeExecute) conditionLabel else bodyLabel) + codeBuilder.markWithLabel(conditionLabel) + var conditionResult = evaluateExpression(condition, scopeDepth + 1)!! + conditionResult = codeBuilder.receivePointedArgument(conditionResult, requirePointer = 0) + + codeBuilder.addCondition(conditionResult, bodyLabel, exitLabel) + evaluateCodeBlock(bodyExpression, startLabel = bodyLabel, nextIterationLabel = conditionLabel, breakLabel = exitLabel, scopeDepth = scopeDepth + 1) + codeBuilder.markWithLabel(exitLabel) + + return null + } + + private fun evaluateIfOperator(element: KtIfExpression, + scopeDepth: Int, + isExpression: Boolean = true): LLVMVariable? { + val conditionResult = evaluateExpression(element.condition, scopeDepth)!! + val conditionNativeResult = codeBuilder.receivePointedArgument(conditionResult, requirePointer = 0) + + return if (isExpression) + executeIfExpression(conditionNativeResult, element.then!!, element.`else`, element, scopeDepth + 1) + else + executeIfBlock(conditionNativeResult, element.then!!, element.`else`, scopeDepth + 1) + } + + private fun executeIfExpression(conditionResult: LLVMSingleValue, + thenExpression: KtExpression, + elseExpression: PsiElement?, + ifExpression: KtIfExpression, + scopeDepth: Int): LLVMVariable? { + val kotlinType = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, ifExpression)!!.type!! + val expressionType = LLVMInstanceOfStandardType("type", kotlinType, LLVMVariableScope(), state).type + val resultVariable = codeBuilder.getNewVariable(expressionType, pointer = 1) + + when (resultVariable.type) { + is LLVMReferenceType -> codeBuilder.allocStaticVar(resultVariable, asValue = true) + else -> codeBuilder.allocStackVar(resultVariable, asValue = true) + } + val thenLabel = codeBuilder.getNewLabel(prefix = "if_then") + val elseLabel = codeBuilder.getNewLabel(prefix = "if_else") + val endLabel = codeBuilder.getNewLabel(prefix = "if_end") + + codeBuilder.addCondition(conditionResult, thenLabel, elseLabel) + codeBuilder.markWithLabel(thenLabel) + val thenResultExpression = evaluateExpression(thenExpression, scopeDepth + 1) ?: throw UnexpectedException("Can't evaluate then in if expression") + val thenResultNativeExpression = codeBuilder.receiveNativeValue(thenResultExpression) + codeBuilder.storeVariable(resultVariable, thenResultNativeExpression) + codeBuilder.addUnconditionalJump(endLabel) + codeBuilder.markWithLabel(elseLabel) + + val elseResultExpression = evaluateExpression(elseExpression, scopeDepth + 1) ?: throw UnexpectedException("Can't evaluate else in if expression") + val elseResultNativeExpression = codeBuilder.receiveNativeValue(elseResultExpression) + codeBuilder.storeVariable(resultVariable, elseResultNativeExpression) + codeBuilder.addUnconditionalJump(endLabel) + codeBuilder.markWithLabel(endLabel) + return resultVariable + } + + private fun executeIfBlock(conditionResult: LLVMSingleValue, + thenExpression: PsiElement, + elseExpression: PsiElement?, + scopeDepth: Int): LLVMVariable? { + val thenLabel = codeBuilder.getNewLabel(prefix = "if") + val elseLabel = codeBuilder.getNewLabel(prefix = "if") + val endLabel = codeBuilder.getNewLabel(prefix = "if") + + codeBuilder.addCondition(conditionResult, thenLabel, if (elseExpression != null) elseLabel else endLabel) + + evaluateCodeBlock(thenExpression, startLabel = thenLabel, nextIterationLabel = endLabel, breakLabel = endLabel, scopeDepth = scopeDepth + 1) + if (elseExpression != null) { + evaluateCodeBlock(elseExpression, startLabel = elseLabel, nextIterationLabel = endLabel, breakLabel = endLabel, scopeDepth = scopeDepth + 1) + } + + codeBuilder.markWithLabel(endLabel) + return null + } + + private fun evaluateValExpression(element: KtProperty, + scopeDepth: Int): LLVMVariable? { + val variable = state.bindingContext.get(BindingContext.VARIABLE, element)!! + val identifier = variable.fqNameSafe.convertToNativeName() + + val assignExpression = evaluateExpression(element.delegateExpressionOrInitializer, scopeDepth) + val expectedExpressionType = LLVMInstanceOfStandardType("", variable.type, state = state) + + val primitivePointer = LLVMMapStandardType(variable.type, state).isPrimitive + + val allocVar = variableManager.receiveVariable(identifier, expectedExpressionType.type, LLVMRegisterScope(), pointer = expectedExpressionType.pointer + 1) + codeBuilder.allocStackVar(allocVar, pointer = true) + + variableManager.addVariable(identifier, allocVar, scopeDepth) + if (assignExpression != null) { + if ((primitivePointer) && (assignExpression.type is LLVMReferenceType)) { + throw UnexpectedException(element.text) + } + addPrimitiveBinaryOperation(KtTokens.EQ, allocVar, assignExpression, null) + } + + return null + } + + private fun evaluateReturnInstruction(element: PsiElement, + scopeDepth: Int): LLVMVariable? { + val next = element.getNextSiblingIgnoringWhitespaceAndComments() + var retVar = evaluateExpression(next, scopeDepth) + val type = retVar?.type ?: LLVMVoidType() + + when (type) { + is LLVMReferenceType -> generateReferenceReturn(retVar!!) + is LLVMNullType -> { + retVar = when (retVar) { + is LLVMConstant -> LLVMConstant(retVar.value, LLVMNullType(returnType!!.type), returnType!!.pointer - 1) + is LLVMVariable -> LLVMVariable(retVar.label, LLVMNullType(returnType!!.type), retVar.kotlinName, retVar.scope, returnType!!.pointer - 1) + else -> throw UnexpectedException("Unknown inheritor of LLVMSingleValue") + } + generateReferenceReturn(retVar!!) + } + is LLVMVoidType -> codeBuilder.addAnyReturn(LLVMVoidType()) + else -> { + val retNativeValue = codeBuilder.receiveNativeValue(retVar!!) + codeBuilder.addReturnOperator(retNativeValue) + } + } + + return null + } + + private fun generateReferenceReturn(retVar: LLVMSingleValue) { + var result = retVar + if (result.pointer == 2) { + result = codeBuilder.loadAndGetVariable(retVar as LLVMVariable) + } + + codeBuilder.storeVariable(returnType!!, result) + codeBuilder.addAnyReturn(LLVMVoidType()) + } +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/ClassCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/ClassCodegen.kt new file mode 100644 index 00000000000..b9f3619474a --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/ClassCodegen.kt @@ -0,0 +1,84 @@ +package org.kotlinnative.translator.codegens + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.resolve.BindingContext +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.exceptions.TranslationException +import org.kotlinnative.translator.llvm.LLVMBuilder +import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType + +class ClassCodegen(state: TranslationState, + variableManager: VariableManager, + val clazz: KtClass, + codeBuilder: LLVMBuilder, + parentCodegen: StructCodegen? = null) : + StructCodegen(state, variableManager, clazz, codeBuilder, parentCodegen) { + + val annotation: Boolean + val enum: Boolean + var companionObjectCodegen: ObjectCodegen? = null + val descriptor: ClassDescriptor + + override var size: Int = 0 + override val structName: String = clazz.fqName?.asString()!! + override val type: LLVMReferenceType + + init { + type = LLVMReferenceType(structName, "class", align = TranslationState.POINTER_ALIGN, size = TranslationState.POINTER_SIZE, byRef = true) + descriptor = state.bindingContext.get(BindingContext.CLASS, clazz) ?: throw TranslationException("Can't receive descriptor of class " + clazz.name) + + annotation = descriptor.kind == ClassKind.ANNOTATION_CLASS + enum = descriptor.kind == ClassKind.ENUM_CLASS + + type.align = TranslationState.POINTER_ALIGN + } + + override fun prepareForGenerate() { + val parameterList = clazz.getPrimaryConstructorParameterList()?.parameters ?: listOf() + indexFields(parameterList) + generateInnerFields(clazz.declarations) + + type.size = calculateTypeSize() + + if (annotation) { + return + } + + super.prepareForGenerate() + nestedClasses.forEach { x, classCodegen -> classCodegen.prepareForGenerate() } + + if (descriptor.companionObjectDescriptor != null) { + val companionObject = clazz.getCompanionObjects().first() + companionObjectCodegen = ObjectCodegen(state, variableManager, companionObject, codeBuilder, this) + companionObjectCodegen!!.prepareForGenerate() + } + } + + override fun generate() { + if (annotation) { + return + } + + super.generate() + nestedClasses.forEach { x, classCodegen -> classCodegen.generate() } + companionObjectCodegen?.generate() + } + + private fun indexFields(parameters: MutableList) { + if (annotation) { + return + } + + val currentConstructorFields = parameters.mapIndexed { i, it -> resolveType(it, state.bindingContext.get(BindingContext.TYPE, it.typeReference)!!, fields.size + i) } + fields.addAll(currentConstructorFields) + fieldsIndex.putAll(currentConstructorFields.map { Pair(it.label, it) }) + primaryConstructorIndex = LLVMType.mangleFunctionArguments(currentConstructorFields) + constructorFields.put(primaryConstructorIndex!!, currentConstructorFields) + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/FunctionCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/FunctionCodegen.kt new file mode 100644 index 00000000000..ed2a4a8df72 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/FunctionCodegen.kt @@ -0,0 +1,138 @@ +package org.kotlinnative.translator.codegens + +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.llvm.* +import org.kotlinnative.translator.llvm.types.LLVMFunctionType +import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType +import org.kotlinnative.translator.llvm.types.LLVMVoidType +import java.util.* + + +class FunctionCodegen(state: TranslationState, + variableManager: VariableManager, + val function: KtNamedFunction, + codeBuilder: LLVMBuilder) : + BlockCodegen(state, variableManager, codeBuilder) { + + var name: String + var args = LinkedList() + val isExtensionDeclaration = function.isExtensionDeclaration() + var functionNamePrefix = "" + val external: Boolean + val defaultValues: List + val fullName: String + get() = functionNamePrefix + name + + init { + val descriptor = state.bindingContext.get(BindingContext.FUNCTION, function)!! + args.addAll(descriptor.valueParameters.map { + LLVMInstanceOfStandardType(it?.fqNameSafe?.asString() ?: it.name.toString(), it.type, state = state) + }) + + returnType = LLVMInstanceOfStandardType("instance", descriptor.returnType!!, state = state) + if (returnType!!.type is LLVMReferenceType) { + returnType!!.pointer = 2 + } + external = descriptor.isExternal + name = if (external) function.name!! else function.fqName!!.convertToNativeName() + LLVMType.mangleFunctionArguments(args) + + if (isExtensionDeclaration) { + name = "${function.name}${if (!external) LLVMType.mangleFunctionArguments(args) else ""}" + val receiverType = descriptor.extensionReceiverParameter!!.type + val translatorType = LLVMMapStandardType(receiverType, state) + val packageName = (function.containingFile as KtFile).packageFqName.asString() + functionNamePrefix = packageName.addAfterIfNotEmpty(".") + translatorType.mangle + "." + + val extensionFunctionsOfThisType = state.extensionFunctions.getOrDefault(translatorType.toString(), HashMap()) + extensionFunctionsOfThisType.put(fullName, this) + state.extensionFunctions.put(translatorType.toString(), extensionFunctionsOfThisType) + } + + defaultValues = descriptor.valueParameters.indices.map { + val parameterDescriptor = descriptor.valueParameters[it] + if (parameterDescriptor.declaresDefaultValue()) { + val initializer = (parameterDescriptor.source as KotlinSourceElement).psi as KtParameter + initializer.defaultValue + } else { + null + } + } + } + + fun generate(this_type: LLVMVariable? = null) { + generateDeclaration(this_type) + + if (external) { + return + } + + codeBuilder.addStartExpression() + generateLoadArguments() + evaluateCodeBlock(function.bodyExpression, scopeDepth = topLevelScopeDepth, isBlock = function.hasBlockBody()) + + if (returnType?.type is LLVMVoidType){ + codeBuilder.addAnyReturn(LLVMVoidType()) + } + codeBuilder.addEndExpression() + } + + private fun generateDeclaration(this_type: LLVMVariable? = null) { + var actualReturnType: LLVMType = returnType!!.type + val actualArgs = ArrayList() + + if (returnType!!.pointer > 0) { + actualReturnType = LLVMVoidType() + actualArgs.add(returnType!!) + } + + if (isExtensionDeclaration) { + val receiverParameter = state.bindingContext.get(BindingContext.FUNCTION, function)!!.extensionReceiverParameter!! + val receiverType = receiverParameter.type + val translatorType = LLVMMapStandardType(receiverType, state) + + val classVal = when (translatorType) { + is LLVMReferenceType -> LLVMVariable("classvariable.this", translatorType, pointer = 1) + else -> LLVMVariable("type", translatorType, pointer = 0) + } + + variableManager.addVariable("this", classVal, level = 0) + actualArgs.add(classVal) + } + + if (this_type != null) { + actualArgs.add(this_type) + } + + actualArgs.addAll(args) + + codeBuilder.addLLVMCodeToLocalPlace(LLVMFunctionDescriptor(fullName, actualArgs, actualReturnType, external)) + } + + private fun generateLoadArguments() { + args.forEach(fun(it: LLVMVariable) { + if (it.type is LLVMFunctionType || (it.type is LLVMReferenceType && it.type.byRef)) { + variableManager.addVariable(it.label, LLVMVariable(it.label, it.type, it.label, LLVMRegisterScope(), pointer = 1), topLevelScopeDepth) + return + } + + if (it.type !is LLVMReferenceType || it.type.byRef) { + val loadVariable = LLVMVariable(it.label, it.type, it.label, LLVMRegisterScope(), pointer = it.pointer) + val allocVar = codeBuilder.loadArgument(loadVariable) + variableManager.addVariable(it.label, allocVar, topLevelScopeDepth) + } else { + variableManager.addVariable(it.label, LLVMVariable(it.label, it.type, it.label, LLVMRegisterScope(), pointer = 0), topLevelScopeDepth) + } + }) + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/ObjectCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/ObjectCodegen.kt new file mode 100644 index 00000000000..4f58ad3d000 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/ObjectCodegen.kt @@ -0,0 +1,43 @@ +package org.kotlinnative.translator.codegens + +import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.llvm.LLVMBuilder +import org.kotlinnative.translator.llvm.LLVMVariable +import org.kotlinnative.translator.llvm.LLVMVariableScope +import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType + +class ObjectCodegen(state: TranslationState, + variableManager: VariableManager, + val objectDeclaration: KtObjectDeclaration, + codeBuilder: LLVMBuilder, + parentCodegen: StructCodegen? = null) : + StructCodegen(state, variableManager, objectDeclaration, codeBuilder, parentCodegen) { + + override var size: Int = 0 + override val structName: String = objectDeclaration.fqName?.asString()!! + override val type = LLVMReferenceType(structName, "class") + + init { + primaryConstructorIndex = LLVMType.mangleFunctionArguments(emptyList()) + constructorFields.put(primaryConstructorIndex!!, arrayListOf()) + } + + override fun prepareForGenerate() { + generateInnerFields(objectDeclaration.declarations) + type.size = calculateTypeSize() + + super.prepareForGenerate() + + val classInstance = LLVMVariable("object.instance.$structName", type, objectDeclaration.name, LLVMVariableScope(), pointer = 1) + codeBuilder.addGlobalInitialize(classInstance, fields, initializedFields.map { + val type = state.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, it.value)!!.type!! + Pair(it.key, state.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, it.value)!!.getValue(type).toString()) + }.toMap(), type) + variableManager.addGlobalVariable(structName, classInstance) + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/PropertyCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/PropertyCodegen.kt new file mode 100644 index 00000000000..ca21816d604 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/PropertyCodegen.kt @@ -0,0 +1,33 @@ +package org.kotlinnative.translator.codegens + +import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.BindingContext +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.llvm.LLVMBuilder +import org.kotlinnative.translator.llvm.LLVMInstanceOfStandardType +import org.kotlinnative.translator.llvm.LLVMVariable +import org.kotlinnative.translator.llvm.LLVMVariableScope + + +class PropertyCodegen(val state: TranslationState, + val variableManager: VariableManager, + val property: KtProperty, + val codeBuilder: LLVMBuilder) { + + fun generate() { + val varInfo = state.bindingContext.get(BindingContext.VARIABLE, property)?.compileTimeInitializer ?: return + + val kotlinType = varInfo.type + val value = varInfo.value + if (kotlinType.nameIfStandardType != null) { + val variableType = LLVMInstanceOfStandardType(property.name ?: return, kotlinType, state = state).type + val variable = LLVMVariable(property.name.toString(), variableType, property.name.toString(), LLVMVariableScope()) + variableManager.addGlobalVariable(property.name.toString(), variable) + codeBuilder.defineGlobalVariable(variable, variableType.parseArg(value.toString())) + variable.pointer++ + } + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/codegens/StructCodegen.kt b/experiments/student-backend/src/org/kotlinnative/translator/codegens/StructCodegen.kt new file mode 100644 index 00000000000..92021d7b054 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/codegens/StructCodegen.kt @@ -0,0 +1,252 @@ +package org.kotlinnative.translator.codegens + +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.VariableManager +import org.kotlinnative.translator.llvm.* +import org.kotlinnative.translator.llvm.types.LLVMCharType +import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType +import org.kotlinnative.translator.llvm.types.LLVMVoidType +import java.util.* + +/* + * TODO make high level description of code generation process + * and structure of generated code. + */ +abstract class StructCodegen(val state: TranslationState, + val variableManager: VariableManager, + val classOrObject: KtClassOrObject, + val codeBuilder: LLVMBuilder, + val parentCodegen: StructCodegen? = null) { + + val fields = ArrayList() + val fieldsIndex = HashMap() + val nestedClasses = HashMap() + val enumFields = HashMap() + val constructorFields = HashMap>() + var primaryConstructorIndex: String? = null + val initializedFields = HashMap() + var methods = HashMap() + + abstract val type: LLVMReferenceType + abstract var size: Int + abstract val structName: String + + open fun prepareForGenerate() { + generateStruct() + classOrObject.declarations.filterIsInstance().map { + val function = FunctionCodegen(state, variableManager, it, codeBuilder) + methods.put(function.name, function) + } + } + + fun calculateTypeSize(): Int { + val classAlignment = fields.map { it.type.align }.max()?.toInt() ?: 0 + var alignmentRemainder = 0 + size = 0 + + for (item in fields) { + val currentFieldSize = if (item.pointer > 0) TranslationState.POINTER_ALIGN else item.type.align + alignmentRemainder -= (alignmentRemainder % currentFieldSize) + if (alignmentRemainder < currentFieldSize) { + size += classAlignment + alignmentRemainder = classAlignment - currentFieldSize + } else { + alignmentRemainder -= currentFieldSize + } + } + return size + } + + open fun generate() { + generateEnumFields() + generatePrimaryConstructor() + classOrObject.getSecondaryConstructors().map { generateSecondaryConstructor(it) } + + val classVal = LLVMVariable("classvariable.this", type, pointer = if (type.isPrimitive) 0 else 1) + variableManager.addVariable("this", classVal, level = 0) + + methods.values.map { it.generate(classVal) } + } + + fun generateInnerFields(declarations: List) { + for (declaration in declarations) { + when (declaration) { + is KtProperty -> { + val ktType = state.bindingContext.get(BindingContext.TYPE, declaration.typeReference) + ?: state.bindingContext.get(BindingContext.VARIABLE, declaration)!!.type + val field = resolveType(declaration, ktType, fields.size) + + if (declaration.initializer != null) { + initializedFields.put(field, declaration.initializer!!) + } + + fields.add(field) + fieldsIndex[field.label] = field + } + is KtEnumEntry -> { + val name = declaration.name!! + val field = LLVMVariable("class.$structName.$name", type, scope = LLVMVariableScope(), pointer = 2) + enumFields.put(name, field) + } + is KtClass -> + nestedClasses.put(declaration.fqName!!.asString(), + ClassCodegen(state, + VariableManager(state.globalVariableCollection), + declaration, codeBuilder, this)) + } + } + } + + private fun generateEnumFields() { + val enumEntries = classOrObject.declarations.filterIsInstance() + + for (declaration in enumEntries) { + val name = declaration.name!! + val initializer = declaration.initializerList!!.initializers[0] + val arguments = (initializer as KtSuperTypeCallEntry).valueArguments.map { it.getArgumentExpression()!!.text } + + val field = codeBuilder.getNewVariable(type, scope = LLVMVariableScope()) + val enumField = enumFields[name]!! + + codeBuilder.defineGlobalVariable(field, codeBuilder.makeStructInitializer(constructorFields[primaryConstructorIndex]!!, arguments)) + codeBuilder.defineGlobalVariable(LLVMVariable(enumField.label, enumField.type, enumField.kotlinName, enumField.scope, enumField.pointer - 1), field.toString()) + } + } + + private fun generateStruct() = + codeBuilder.createClass(structName, fields) + + private fun generateSecondaryConstructor(secondaryConstructor: KtSecondaryConstructor) { + val thisCall = secondaryConstructor.getDelegationCall().calleeExpression + val descriptor = state.bindingContext.get(BindingContext.CONSTRUCTOR, secondaryConstructor) + + val classVal = LLVMVariable("classvariable.this", type, pointer = 1) + variableManager.addVariable("this", classVal, level = 0) + + val secondaryConstructorArguments = descriptor!!.valueParameters.map { + LLVMInstanceOfStandardType(it.fqNameSafe.convertToNativeName(), it.type, state = state) + } + + val argFields = mutableListOf(classVal) + argFields.addAll(secondaryConstructorArguments) + val currentConstructorIndex = LLVMType.mangleFunctionArguments(secondaryConstructorArguments) + constructorFields.put(currentConstructorIndex, argFields) + codeBuilder.addLLVMCodeToLocalPlace(LLVMFunctionDescriptor(structName + currentConstructorIndex, argFields, LLVMVoidType())) + + codeBuilder.addStartExpression() + + secondaryConstructorArguments.map { variableManager.addVariable(it.label, it, level = 2) } + + val blockCodegen = object : BlockCodegen(state, variableManager, codeBuilder) {} + val mainConstructorThis = blockCodegen.evaluateConstructorDelegationReferenceExpression(thisCall!!, this, secondaryConstructorArguments, 1) as LLVMVariable + variableManager.addVariable("this", mainConstructorThis, level = 0) + + blockCodegen.evaluateCodeBlock(secondaryConstructor.bodyExpression, scopeDepth = 1) + generateReturn(codeBuilder.receivePointedArgument(variableManager["this"]!!, requirePointer = 1) as LLVMVariable) + codeBuilder.addAnyReturn(LLVMVoidType()) + codeBuilder.addEndExpression() + } + + private fun generatePrimaryConstructor() { + val classVal = LLVMVariable("classvariable.this", type, pointer = 1) + variableManager.addVariable("this", classVal, level = 0) + + val argFields = mutableListOf(classVal) + argFields.addAll(constructorFields[primaryConstructorIndex]!!) + + codeBuilder.addLLVMCodeToLocalPlace(LLVMFunctionDescriptor(structName + primaryConstructorIndex, argFields, LLVMVoidType())) + + codeBuilder.addStartExpression() + generateLoadArguments(classVal) + generateAssignments() + generateReturn(LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1)) + genClassInitializers() + codeBuilder.addAnyReturn(LLVMVoidType()) + codeBuilder.addEndExpression() + } + + private fun generateLoadArguments(thisField: LLVMVariable) { + val thisVariable = LLVMVariable(thisField.label, thisField.type, thisField.label, LLVMRegisterScope(), pointer = 0) + codeBuilder.loadArgument(thisVariable, false) + + constructorFields[primaryConstructorIndex]!!.filter { it.type !is LLVMReferenceType }.forEach { + val loadVariable = LLVMVariable(it.label, it.type, it.label, LLVMRegisterScope()) + codeBuilder.loadArgument(loadVariable) + } + } + + private fun generateAssignments() { + constructorFields[primaryConstructorIndex]!!.forEach { + when (it.type) { + is LLVMReferenceType -> { + val classField = codeBuilder.getNewVariable(it.type, pointer = it.pointer + 1) + codeBuilder.loadClassField(classField, LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1), (it as LLVMClassVariable).offset) + codeBuilder.storeVariable(classField, it) + } + else -> { + val argument = codeBuilder.loadAndGetVariable(LLVMVariable("${it.label}.addr", it.type, scope = LLVMRegisterScope(), pointer = it.pointer + 1)) + val classField = codeBuilder.getNewVariable(it.type, pointer = 1) + codeBuilder.loadClassField(classField, LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1), (it as LLVMClassVariable).offset) + codeBuilder.storeVariable(classField, argument) + } + } + } + + val blockCodegen = object : BlockCodegen(state, variableManager, codeBuilder) {} + val receiverThis = LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1) + variableManager.addVariable("this", receiverThis, level = 2) + + for ((variable, initializer) in initializedFields) { + val left = blockCodegen.evaluateMemberMethodOrField(receiverThis, variable.label, blockCodegen.topLevelScopeDepth, call = null)!! + val right = blockCodegen.evaluateExpression(initializer, scopeDepth = blockCodegen.topLevelScopeDepth)!! + blockCodegen.addPrimitiveBinaryOperation(KtTokens.EQ, left, right) + } + + variableManager.pullOneUpwardLevelVariable("this") + } + + private fun generateReturn(src: LLVMVariable) { + val dst = LLVMVariable("classvariable.this", type, scope = LLVMRegisterScope(), pointer = 1) + + val castedDst = codeBuilder.bitcast(dst, LLVMCharType(), pointer = 1) + val castedSrc = codeBuilder.bitcast(src, LLVMCharType(), pointer = 1) + + codeBuilder.memcpy(castedDst, castedSrc, size) + } + + protected fun resolveType(field: KtNamedDeclaration, ktType: KotlinType, offset: Int): LLVMClassVariable { + val annotations = parseFieldAnnotations(field) + val fieldName = state.bindingContext.get(BindingContext.VALUE_PARAMETER, field as?KtParameter)?.fqNameSafe?.convertToNativeName() + ?: field.fqName?.asString() + ?: field.name!! + + val result = LLVMInstanceOfStandardType(fieldName, ktType, LLVMRegisterScope(), state = state) + + if (state.classes.containsKey(field.name!!)) { + return LLVMClassVariable(result.label, state.classes[fieldName]!!.type, result.pointer) + } + + if (annotations.contains("Plain")) { + result.pointer = 0 + } + + return LLVMClassVariable(result.label, result.type, result.pointer, offset) + } + + private fun parseFieldAnnotations(field: KtNamedDeclaration): Set = + field.annotationEntries.map { state.bindingContext.get(BindingContext.ANNOTATION, it)?.type.toString() }.toHashSet() + + protected fun genClassInitializers() = + classOrObject.getAnonymousInitializers().map { + object : BlockCodegen(state, variableManager, codeBuilder) { + fun generate() = evaluateCodeBlock(it.body, scopeDepth = topLevelScopeDepth) + } + }.map { it.generate() } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/debug/debug.kt b/experiments/student-backend/src/org/kotlinnative/translator/debug/debug.kt new file mode 100644 index 00000000000..345a7416faa --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/debug/debug.kt @@ -0,0 +1,59 @@ +package org.kotlinnative.translator.debug + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.CompositeElement +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedFunction + +fun debugPrintNode(node: Any?, indent: Int = 0): Unit { + if (node == null) { + return + } + + for (i in 0..indent) { + print(" ") + } + + println(node.toString()) + + when (node) { + is CompositeElement -> { + debugPrintNode(node.firstChildNode, indent + 2) + debugPrintNode(node.treeNext, indent) + } + is PsiElement -> { + debugPrintNode(node.firstChild, indent + 2) + debugPrintNode(node.nextSibling, indent) + } + is PsiWhiteSpace -> { + debugPrintNode(node.firstChild, indent + 2) + debugPrintNode(node.nextSibling, indent) + } + else -> { + print(" not supported: " + node.javaClass.canonicalName) + } + } + +} + +fun printFunction(function: KtNamedFunction) { + debugPrintNode(function.node) +} + +fun printClass(function: KtClass) { + debugPrintNode(function.node) +} + +fun printFile(file: KtFile) { + + for (declaration in file.declarations) { + + when (declaration) { + is KtNamedFunction -> printFunction(declaration) + is KtClass -> printClass(declaration) + else -> println(declaration.toString()) + } + } +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/exceptions/TranslationException.kt b/experiments/student-backend/src/org/kotlinnative/translator/exceptions/TranslationException.kt new file mode 100644 index 00000000000..4b132fcbbcc --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/exceptions/TranslationException.kt @@ -0,0 +1,5 @@ +package org.kotlinnative.translator.exceptions + +import java.lang.Exception + +class TranslationException(message: String) : Exception(message) diff --git a/experiments/student-backend/src/org/kotlinnative/translator/exceptions/UnimplementedException.kt b/experiments/student-backend/src/org/kotlinnative/translator/exceptions/UnimplementedException.kt new file mode 100644 index 00000000000..e4b2b0dd4bc --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/exceptions/UnimplementedException.kt @@ -0,0 +1,5 @@ +package org.kotlinnative.translator.exceptions + +import java.lang.Exception + +class UnimplementedException() : Exception() diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMBuilder.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMBuilder.kt new file mode 100644 index 00000000000..894419bdc56 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMBuilder.kt @@ -0,0 +1,252 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.llvm.types.* +import java.rmi.UnexpectedException +import java.util.* + +class LLVMBuilder(arm: Boolean = false) { + + private val localCode: StringBuilder = StringBuilder() + private val globalCode: StringBuilder = StringBuilder() + private var variableCount = 0 + private var labelCount = 0 + private val exceptions = mapOf( + Pair("KotlinNullPointerException", initializeExceptionString("Exception in thread main kotlin.KotlinNullPointerException"))) + + + init { + val declares = arrayOf( + "declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1)", + "declare i8* @malloc_heap(i32)", + "declare i32 @printf(i8*, ...)", + "declare void @abort()", + "%class.Nothing = type { }") + + declares.forEach { addLLVMCodeToGlobalPlace(it) } + + if (arm) { + val functionAttributes = """attributes #0 = { nounwind "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+hwdiv,+strict-align" }""" + addLLVMCodeToGlobalPlace(functionAttributes) + } + } + + fun addLLVMCodeToLocalPlace(code: String) = + localCode.appendln(code) + + fun addLLVMCodeToGlobalPlace(code: String) = + globalCode.appendln(code) + + fun addComment(comment: String) = + addLLVMCodeToLocalPlace("; " + comment) + + fun addStartExpression() = + addLLVMCodeToLocalPlace("{") + + fun addEndExpression() = + addLLVMCodeToLocalPlace("unreachable\n}") + + fun addAssignment(lhs: LLVMVariable, rhs: LLVMNode) = + addLLVMCodeToLocalPlace("$lhs = $rhs") + + fun addReturnOperator(llvmVariable: LLVMSingleValue) = + addLLVMCodeToLocalPlace("ret ${llvmVariable.type} $llvmVariable") + + fun addAnyReturn(type: LLVMType, value: String = type.defaultValue, pointer: Int = 0) = + addLLVMCodeToLocalPlace("ret $type${"*".repeat(pointer)} $value") + + fun addStringConstant(variable: LLVMVariable, value: String) = + addLLVMCodeToGlobalPlace("$variable = private unnamed_addr constant ${(variable.type as LLVMStringType).fullArrayType} c\"${value.replace("\"", "\\\"")}\\00\", align 1") + + fun addCondition(condition: LLVMSingleValue, thenLabel: LLVMLabel, elseLabel: LLVMLabel) = + addLLVMCodeToLocalPlace("br ${condition.pointedType} $condition, label $thenLabel, label $elseLabel") + + fun addUnconditionalJump(label: LLVMLabel) = + addLLVMCodeToLocalPlace("br label $label") + + fun addExceptionCall(exceptionName: String) { + val exception = exceptions[exceptionName] + val printResult = getNewVariable(LLVMIntType(), pointer = 0) + addLLVMCodeToLocalPlace("$printResult = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds (${(exception!!.type as LLVMStringType).fullArrayType}* $exception, i32 0, i32 0))") + addFunctionCall(LLVMVariable("abort", LLVMVoidType(), scope = LLVMVariableScope()), emptyList()) + } + + fun addFunctionCall(functionName: LLVMVariable, arguments: List) = + addLLVMCodeToLocalPlace("call ${functionName.type} $functionName(${arguments.joinToString { it -> "${it.type} $it" }})") + + fun allocStackVar(target: LLVMVariable, asValue: Boolean = false, pointer: Boolean = false) { + val type = if (asValue) target.type.toString() else target.pointedType + addLLVMCodeToLocalPlace("$target = alloca ${if (pointer) type.removeSuffix("*") else type}, align ${target.type.align}") + } + + fun allocStaticVar(target: LLVMVariable, asValue: Boolean = false, pointer: Boolean = false) { + val allocated = getNewVariable(LLVMCharType(), pointer = 1) + val size = if ((target.pointer >= 2) || (target.pointer >= 1 && !pointer)) TranslationState.POINTER_SIZE else target.type.size + + addLLVMCodeToLocalPlace("$allocated = call i8* @malloc_heap(i32 $size)") + addLLVMCodeToLocalPlace("$target = bitcast ${allocated.pointedType} $allocated to ${if (asValue) target.type.toString() else target.pointedType}" + if (pointer) "" else "*") + } + + fun addGlobalInitialize(target: LLVMVariable, fields: ArrayList, initializers: Map, classType: LLVMType) { + val code = "$target = internal global $classType { ${ + fields.map { it.pointedType + " " + if (initializers.containsKey(it)) initializers[it] else "0" }.joinToString() + } }, align ${classType.align}" + addLLVMCodeToGlobalPlace(code) + } + + fun bitcast(src: LLVMVariable, llvmType: LLVMType, pointer: Int): LLVMVariable { + val empty = getNewVariable(llvmType, pointer = pointer) + addLLVMCodeToLocalPlace("$empty = bitcast ${src.pointedType} $src to ${llvmType.toString() + "*".repeat(pointer)}") + return empty + } + + fun convertVariableToType(variable: LLVMSingleValue, targetType: LLVMType): LLVMSingleValue { + var resultVariable = variable + if (variable.type != targetType) { + val convertedExpression = targetType.convertFrom(variable) + resultVariable = saveExpression(convertedExpression) + } + return resultVariable + } + + fun copyVariable(from: LLVMVariable, to: LLVMVariable) = + when { + from.type is LLVMStringType && !from.type.isLoaded -> storeString(to, from, 0) + else -> copyVariableValue(to, from) + } + + fun createClass(name: String, fields: List) = + addLLVMCodeToGlobalPlace("%class.$name = type { ${fields.map { it.pointedType }.joinToString()} }") + + fun declareEntryPoint(name: String) { + addLLVMCodeToLocalPlace("define weak void @main()") + addStartExpression() + addFunctionCall(LLVMVariable(name, LLVMVoidType(), scope = LLVMVariableScope()), listOf()) + addAnyReturn(LLVMVoidType()) + addEndExpression() + } + + fun defineGlobalVariable(variable: LLVMVariable, defaultValue: String = variable.type.defaultValue) = + addLLVMCodeToLocalPlace("$variable = global ${variable.pointedType} $defaultValue, align ${variable.type.align}") + + fun getNewVariable(type: LLVMType, pointer: Int = 0, kotlinName: String? = null, scope: LLVMScope = LLVMRegisterScope(), prefix: String = "var"): LLVMVariable { + variableCount++ + return LLVMVariable("$prefix$variableCount", type, kotlinName, scope, pointer) + } + + fun getNewLabel(scope: LLVMScope = LLVMRegisterScope(), prefix: String): LLVMLabel { + labelCount++ + return LLVMLabel("label.$prefix.$labelCount", scope) + } + + fun loadArgsIfRequired(names: List, args: List) = + names.mapIndexed(fun(i: Int, value: LLVMSingleValue): LLVMSingleValue { + return receivePointedArgument(value, args[i].pointer) + }).toList() + + fun loadArgument(llvmVariable: LLVMVariable, store: Boolean = true): LLVMVariable { + val allocVar = LLVMVariable("${llvmVariable.label}.addr", llvmVariable.type, llvmVariable.kotlinName, LLVMRegisterScope(), pointer = llvmVariable.pointer + 1) + allocStackVar(allocVar, asValue = false, pointer = true) + if (store) { + storeVariable(allocVar, llvmVariable) + } + return allocVar + } + + fun loadClassField(target: LLVMVariable, source: LLVMVariable, offset: Int) = + addLLVMCodeToLocalPlace("$target = getelementptr inbounds ${source.pointedType} $source, i32 0, i32 $offset") + + fun loadVariableOffset(target: LLVMVariable, source: LLVMVariable, index: LLVMConstant) = + addLLVMCodeToLocalPlace("$target = getelementptr inbounds ${source.type} $source, ${index.type} ${index.value}") + + fun loadAndGetVariable(source: LLVMVariable): LLVMVariable { + assert(source.pointer > 0) + val target = getNewVariable(source.type, source.pointer - 1, source.kotlinName) + addLLVMCodeToLocalPlace("$target = load ${source.pointedType} $source, align ${target.type.align}") + return target + } + + fun makeStructInitializer(args: List, values: List) + = "{ ${args.mapIndexed { i: Int, variable: LLVMVariable -> "${variable.type} ${values[i]}" }.joinToString()} }" + + fun markWithLabel(label: LLVMLabel?) { + if (label != null) + addLLVMCodeToLocalPlace("${label.label}:") + } + + fun memcpy(castedDst: LLVMVariable, castedSrc: LLVMVariable, size: Int, align: Int = 4, volatile: Boolean = false) = + addLLVMCodeToLocalPlace("call void @llvm.memcpy.p0i8.p0i8.i64(i8* $castedDst, i8* $castedSrc, i64 $size, i32 $align, i1 $volatile)") + + fun nullCheck(variable: LLVMVariable): LLVMVariable { + val result = getNewVariable(LLVMBooleanType(), pointer = 0) + val loaded = loadAndGetVariable(variable) + addLLVMCodeToLocalPlace("$result = icmp eq ${loaded.pointedType} null, $loaded") + return result + } + + fun receiveNativeValue(firstOp: LLVMSingleValue): LLVMSingleValue = + when { + firstOp is LLVMConstant || firstOp.pointer == 0 -> firstOp + firstOp is LLVMVariable -> loadAndGetVariable(firstOp) + else -> throw UnexpectedException("Unknown inheritor of LLVMSingleValue") + } + + fun receivePointedArgument(value: LLVMSingleValue, requirePointer: Int): LLVMSingleValue { + var result = value + + while (result.pointer > requirePointer) { + result = receiveNativeValue(result) + } + + if ((value.type is LLVMStringType) && !(value.type.isLoaded)) { + val newVariable = getNewVariable(value.type, pointer = result.pointer + 1) + allocStackVar(newVariable, asValue = true) + copyVariable(result as LLVMVariable, newVariable) + result = loadAndGetVariable(newVariable) + } + + return result + } + + fun storeString(target: LLVMVariable, source: LLVMVariable, offset: Int) { + val code = "store ${target.type} getelementptr inbounds (" + + "${(source.type as LLVMStringType).fullArrayType}* $source, i32 0, i32 $offset), ${target.pointedType} $target, align ${source.type.align}" + (target.type as LLVMStringType).isLoaded = true + addLLVMCodeToLocalPlace(code) + } + + fun storeVariable(target: LLVMSingleValue, source: LLVMSingleValue) { + if ((source.type is LLVMStringType) && !(source.type.isLoaded)) { + storeString(target as LLVMVariable, source as LLVMVariable, 0) + } else { + addLLVMCodeToLocalPlace("store ${source.pointedType} $source, ${target.pointedType} $target, align ${source.type.align}") + } + } + + fun saveExpression(expression: LLVMSingleValue): LLVMVariable { + val resultOp = getNewVariable(expression.type, pointer = expression.pointer) + addAssignment(resultOp, expression) + return resultOp + } + + fun storeNull(result: LLVMVariable) = + addLLVMCodeToLocalPlace("store ${result.pointedType.dropLast(1)} null, ${result.pointedType} $result, align ${TranslationState.POINTER_ALIGN}") + + override fun toString() = globalCode.toString() + localCode.toString() + + private fun copyVariableValue(target: LLVMVariable, source: LLVMVariable) { + var from = source + if (source.pointer > 0) { + from = getNewVariable(source.type, source.pointer) + addLLVMCodeToLocalPlace("$from = load ${source.pointedType} $source, align ${from.type.align}") + } + addLLVMCodeToLocalPlace("store ${target.type} $from, ${target.pointedType} $target, align ${from.type.align}") + } + + private fun initializeExceptionString(string: String): LLVMVariable { + val result = getNewVariable(LLVMStringType(string.length), pointer = 0, scope = LLVMVariableScope(), prefix = "exceptions.str.") + addStringConstant(result, string) + return result + } + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMCall.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMCall.kt new file mode 100644 index 00000000000..b704798aa0d --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMCall.kt @@ -0,0 +1,11 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +class LLVMCall(val returnType: LLVMType, + val name: String, + val arguments: Collection) : LLVMSingleValue(returnType) { + + override fun toString() = "call $returnType $name(${arguments.joinToString { "${it.pointedType} ${it.toString()}" }})" + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMClassVariable.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMClassVariable.kt new file mode 100644 index 00000000000..780fb40d759 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMClassVariable.kt @@ -0,0 +1,8 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +class LLVMClassVariable(label: String, + type: LLVMType, + pointer: Int = 0, + var offset: Int = 0) : LLVMVariable(label, type, pointer = pointer) \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMConstant.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMConstant.kt new file mode 100644 index 00000000000..17a4bd69af2 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMConstant.kt @@ -0,0 +1,13 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +open class LLVMConstant(value: String, + type: LLVMType, + pointer: Int = 0) : LLVMSingleValue(type, pointer) { + + val value = type.parseArg(value) + + override fun toString() = value + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMExpression.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMExpression.kt new file mode 100644 index 00000000000..bd67bac1daa --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMExpression.kt @@ -0,0 +1,9 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +class LLVMExpression(type: LLVMType, val llvmCode: String, pointer: Int = 0) : LLVMSingleValue(type, pointer) { + + override fun toString() = llvmCode + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMLabel.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMLabel.kt new file mode 100644 index 00000000000..ea6e23d2a07 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMLabel.kt @@ -0,0 +1,7 @@ +package org.kotlinnative.translator.llvm + +class LLVMLabel(val label: String, val scope: LLVMScope) : LLVMNode() { + + override fun toString() = "$scope$label" + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMNode.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMNode.kt new file mode 100644 index 00000000000..402b191f78e --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMNode.kt @@ -0,0 +1,3 @@ +package org.kotlinnative.translator.llvm + +abstract class LLVMNode \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMScope.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMScope.kt new file mode 100644 index 00000000000..6932d837bc9 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMScope.kt @@ -0,0 +1,11 @@ +package org.kotlinnative.translator.llvm + +abstract class LLVMScope + +class LLVMVariableScope : LLVMScope() { + override fun toString() = "@" +} + +class LLVMRegisterScope : LLVMScope() { + override fun toString() = "%" +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMSingleValue.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMSingleValue.kt new file mode 100644 index 00000000000..5a8ccc6ff71 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMSingleValue.kt @@ -0,0 +1,10 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +open class LLVMSingleValue(val type: LLVMType, var pointer: Int = 0) : LLVMNode() { + + open val pointedType: String + get() = type.toString() + "*".repeat(pointer) + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMVariable.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMVariable.kt new file mode 100644 index 00000000000..342a12977db --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/LLVMVariable.kt @@ -0,0 +1,13 @@ +package org.kotlinnative.translator.llvm + +import org.kotlinnative.translator.llvm.types.LLVMType + +open class LLVMVariable(val label: String, + type: LLVMType, + var kotlinName: String? = null, + val scope: LLVMScope = LLVMRegisterScope(), + pointer: Int = 0) : LLVMSingleValue(type, pointer) { + + override fun toString() = "$scope$label" + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/generators.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/generators.kt new file mode 100644 index 00000000000..ea1b234f28a --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/generators.kt @@ -0,0 +1,63 @@ +package org.kotlinnative.translator.llvm + +import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype +import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.llvm.types.* + + +fun LLVMFunctionDescriptor(name: String, + argTypes: List?, + returnType: LLVMType, + declare: Boolean = false) = + "${if (declare) "declare" else "define weak"} $returnType @$name(${ + argTypes?.mapIndexed { i: Int, s: LLVMVariable -> + "${s.pointedType} ${if (s.type is LLVMReferenceType && !s.type.byRef) "byval" else ""} %${s.label}" + }?.joinToString()}) #0" + +fun LLVMInstanceOfStandardType(name: String, + type: KotlinType, + scope: LLVMScope = LLVMRegisterScope(), + state: TranslationState): LLVMVariable { + val typeName = type.toString().dropLastWhile { it == '?' } + val pointerMark = if (type.isMarkedNullable) 1 else 0 + return when { + type.isFunctionTypeOrSubtype -> LLVMVariable(name, LLVMFunctionType(type, state), name, scope, pointer = 1) + typeName == "Boolean" -> LLVMVariable(name, LLVMBooleanType(), name, scope, pointerMark) + typeName == "Byte" -> LLVMVariable(name, LLVMByteType(), name, scope, pointerMark) + typeName == "Char" -> LLVMVariable(name, LLVMCharType(), name, scope, pointerMark) + typeName == "Short" -> LLVMVariable(name, LLVMShortType(), name, scope, pointerMark) + typeName == "Int" -> LLVMVariable(name, LLVMIntType(), name, scope, pointerMark) + typeName == "Long" -> LLVMVariable(name, LLVMLongType(), name, scope, pointerMark) + typeName == "Float" -> LLVMVariable(name, LLVMFloatType(), name, scope, pointerMark) + typeName == "Double" -> LLVMVariable(name, LLVMDoubleType(), name, scope, pointerMark) + typeName == "String" -> LLVMVariable(name, LLVMStringType(0), name, scope, pointerMark) + type.nameIfStandardType.toString() == "Nothing" -> LLVMVariable(name, LLVMNullType(), name, scope) + type.isUnit() -> LLVMVariable("", LLVMVoidType(), name, scope) + type.isMarkedNullable -> LLVMVariable(name, LLVMReferenceType(typeName, prefix = "class"), name, scope, pointer = pointerMark) + else -> { + val declarationDescriptor = type.constructor.declarationDescriptor!! + val refName = declarationDescriptor.fqNameSafe.asString() + val refType = state.classes[type.toString()]?.type ?: LLVMReferenceType(refName, align = TranslationState.POINTER_ALIGN, prefix = "class") + + LLVMVariable(name, refType, name, scope, pointer = 1) + } + } +} + +fun LLVMMapStandardType(type: KotlinType, + state: TranslationState) = + LLVMInstanceOfStandardType("type", type, LLVMRegisterScope(), state).type + +fun String.addBeforeIfNotEmpty(add: String): String = + if (this.length > 0) add + this else this + +fun String.addAfterIfNotEmpty(add: String): String = + if (this.length > 0) this + add else this + +fun FqName.convertToNativeName(): String = + this.asString().replace(".", "") \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMArray.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMArray.kt new file mode 100644 index 00000000000..7176186a117 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMArray.kt @@ -0,0 +1,9 @@ +package org.kotlinnative.translator.llvm.types + +interface LLVMArray { + + val arrayElementType: LLVMType + val fullArrayType: String + val length: Int + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMBooleanType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMBooleanType.kt new file mode 100644 index 00000000000..856a030be72 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMBooleanType.kt @@ -0,0 +1,69 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue +import java.lang.IllegalArgumentException + + +class LLVMBooleanType() : LLVMType() { + + override val align = 4 + override var size: Int = 1 + override val defaultValue = "0" + override val mangle = "Boolean" + override val isPrimitive = true + override val typename = "i1" + + override fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "sub nsw i1 $firstOp, $secondOp") + + override fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "mul nsw i1 $firstOp, $secondOp") + + override fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "add nsw i1 $firstOp, $secondOp") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "sdiv i1 $firstOp, $secondOp") + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i1 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i1 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i1 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i1 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i1" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i1" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorOr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "or i1 $firstOp, $secondOp") + + override fun operatorAnd(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "and i1 $firstOp, $secondOp") + + override fun operatorXor(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "xor i1 $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMBooleanType + + override fun parseArg(inputArg: String) = + when (inputArg.toLowerCase()) { + "true" -> "1" + "false" -> "0" + else -> throw IllegalArgumentException("Failed to parse boolean type") + } + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMByteType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMByteType.kt new file mode 100644 index 00000000000..372b3c1cfe7 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMByteType.kt @@ -0,0 +1,42 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + +class LLVMByteType() : LLVMType() { + + override val align = 1 + override var size: Int = 1 + override val mangle = "Byte" + override val typename = "i8" + override val defaultValue = "0" + override val isPrimitive = true + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i8 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i8 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i8 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i8 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i8" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i8" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMByteType(), "srem i8 $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMByteType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMCharType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMCharType.kt new file mode 100644 index 00000000000..75dbd3bc2c5 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMCharType.kt @@ -0,0 +1,41 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + +class LLVMCharType() : LLVMType() { + + override val align = 1 + override var size: Int = 1 + override val mangle = "Char" + override val typename = "i8" + override val defaultValue = "0" + override val isPrimitive = true + + override fun parseArg(inputArg: String) = inputArg.first().toInt().toString() + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i8 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i8 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i8 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i8 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i8" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i8" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMCharType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMDoubleType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMDoubleType.kt new file mode 100644 index 00000000000..f2530e92d11 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMDoubleType.kt @@ -0,0 +1,61 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + + +class LLVMDoubleType() : LLVMType() { + + override val align = 8 + override var size: Int = 8 + override val mangle = "Double" + override val typename = "double" + override val defaultValue = "0.0" + override val isPrimitive = true + + override fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fsub double $firstOp, $secondOp") + + override fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fmul double $firstOp, $secondOp") + + override fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fadd double $firstOp, $secondOp") + + override fun operatorInc(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fadd double $firstOp, 1.0") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fdiv double $firstOp, $secondOp") + + override fun operatorDec(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fsub double $firstOp, 1.0") + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp olt double $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp ogt double $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp ole double i32 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp oge double i32 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp oeq double $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp one double $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "frem double $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMDoubleType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFloatType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFloatType.kt new file mode 100644 index 00000000000..b98b18d0b59 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFloatType.kt @@ -0,0 +1,61 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + + +class LLVMFloatType() : LLVMType() { + + override val align = 4 + override var size: Int = 4 + override val mangle = "Float" + override val typename = "float" + override val defaultValue = "0.0" + override val isPrimitive = true + + override fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMFloatType(), "fsub float $firstOp, $secondOp") + + override fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMFloatType(), "fmul float $firstOp, $secondOp") + + override fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMFloatType(), "fadd float $firstOp, $secondOp") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMFloatType(), "fdiv float $firstOp, $secondOp") + + override fun operatorInc(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fadd float $firstOp, 1.0") + + override fun operatorDec(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMDoubleType(), "fsub float $firstOp, 1.0") + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp olt float $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp ogt float $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp ole float i32 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp oge float i32 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp oeq float" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "fcmp one float" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMFloatType(), "frem float $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMFloatType + + override fun hashCode() = + typename.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFunctionType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFunctionType.kt new file mode 100644 index 00000000000..06304e8c0e1 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMFunctionType.kt @@ -0,0 +1,37 @@ +package org.kotlinnative.translator.llvm.types + +import org.jetbrains.kotlin.types.KotlinType +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.llvm.LLVMInstanceOfStandardType +import org.kotlinnative.translator.llvm.LLVMVariable + +class LLVMFunctionType(type: KotlinType, state: TranslationState) : LLVMType() { + + override val defaultValue = "" + override val align: Int = 4 + override var size: Int = 4 + override val mangle: String + override val typename = "FunctionType" + + val arguments: List + val returnType: LLVMVariable + + init { + val types = type.arguments.map { LLVMInstanceOfStandardType("", it.type, state = state) }.toList() + returnType = types.last() + arguments = types.dropLast(1) + mangle = "F.${LLVMType.mangleFunctionArguments(arguments)}.EF" + } + + fun mangleArgs() = LLVMType.mangleFunctionArguments(arguments) + + override fun toString() = + "${returnType.type} (${arguments.map { it.pointedType }.joinToString()})" + + override fun equals(other: Any?) = + (other is LLVMFunctionType) && (mangle == other.mangle) + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMIntType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMIntType.kt new file mode 100644 index 00000000000..efd61f3a4ed --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMIntType.kt @@ -0,0 +1,89 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.exceptions.UnimplementedException +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + + +class LLVMIntType() : LLVMType() { + + override val align = 4 + override var size: Int = 4 + override val defaultValue = "0" + override val mangle = "Int" + override val typename = "i32" + override val isPrimitive = true + + override fun convertFrom(source: LLVMSingleValue) = + when (source.type) { + is LLVMBooleanType, + is LLVMByteType, + is LLVMCharType, + is LLVMShortType -> LLVMExpression(LLVMIntType(), " sext ${source.type} $source to i32") + else -> throw UnimplementedException() + } + + override fun operatorOr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "or i32 $firstOp, $secondOp") + + override fun operatorAnd(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "and i32 $firstOp, $secondOp") + + override fun operatorXor(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "xor i32 $firstOp, $secondOp") + + override fun operatorShl(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "shl i32 $firstOp, $secondOp") + + override fun operatorShr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "ashr i32 $firstOp, $secondOp") + + override fun operatorUshr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "lshr i32 $firstOp, $secondOp") + + override fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "sub nsw i32 $firstOp, $secondOp") + + override fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "mul nsw i32 $firstOp, $secondOp") + + override fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "add nsw i32 $firstOp, $secondOp") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "sdiv i32 $firstOp, $secondOp") + + override fun operatorInc(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "add nsw i32 $firstOp, 1") + + override fun operatorDec(firstOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "sub nsw i32 $firstOp, 1") + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i32 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i32 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i32 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i32 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i32" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i32" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMIntType(), "srem i32 $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMIntType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMLongType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMLongType.kt new file mode 100644 index 00000000000..eafcde9c8ef --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMLongType.kt @@ -0,0 +1,84 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.exceptions.UnimplementedException +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + + +class LLVMLongType() : LLVMType() { + + override val align = 8 + override var size = 8 + override val defaultValue = "0" + override val typename = "i64" + override val mangle = "Long" + override val isPrimitive = true + + override fun convertFrom(source: LLVMSingleValue) = + when (source.type) { + is LLVMBooleanType, + is LLVMByteType, + is LLVMCharType, + is LLVMShortType, + is LLVMIntType -> LLVMExpression(LLVMLongType(), " sext ${source.type} $source to i64") + else -> throw UnimplementedException() + } + + override fun operatorOr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "or i64 $firstOp, $secondOp") + + override fun operatorAnd(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "and i64 $firstOp, $secondOp") + + override fun operatorXor(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "xor i64 $firstOp, $secondOp") + + override fun operatorShl(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "shl i64 $firstOp, $secondOp") + + override fun operatorShr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "ashr i64 $firstOp, $secondOp") + + override fun operatorUshr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "lshr i64 $firstOp, $secondOp") + + override fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "sub nsw i64 $firstOp, $secondOp") + + override fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "mul nsw i64 $firstOp, $secondOp") + + override fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "add nsw i64 $firstOp, $secondOp") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "sdiv i64 $firstOp, $secondOp") + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i64 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i64 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i64 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i64 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i64" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i64" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMLongType(), "srem i64 $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMLongType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMNullType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMNullType.kt new file mode 100644 index 00000000000..c5d3c935c2b --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMNullType.kt @@ -0,0 +1,20 @@ +package org.kotlinnative.translator.llvm.types + +class LLVMNullType(var baseType: LLVMType? = null) : LLVMType() { + + override val align = 1 + override var size = 0 + override val defaultValue = "null" + override val mangle = "" + override val typename = baseType?.typename.orEmpty() + + override fun parseArg(inputArg: String) = "null" + override fun toString() = baseType?.toString().orEmpty() + + override fun equals(other: Any?) = + other is LLVMNullType + + override fun hashCode() = + "null".hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt new file mode 100644 index 00000000000..86182e1ef4c --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt @@ -0,0 +1,32 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.TranslationState +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue +import org.kotlinnative.translator.llvm.addAfterIfNotEmpty + + +class LLVMReferenceType(val type: String, + var prefix: String = "", + override var align: Int = TranslationState.POINTER_ALIGN, + override var size: Int = TranslationState.POINTER_SIZE, + var byRef: Boolean = true) : LLVMType() { + + override val defaultValue: String = "null" + override val mangle = "Ref_$type" + override val typename = prefix.addAfterIfNotEmpty(".") + type + + override fun toString() = "%$typename" + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq ${firstOp.pointedType} $firstOp, ${if (secondOp.type is LLVMNullType) "null" else "$secondOp"}") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne ${firstOp.pointedType} $firstOp, ${if (secondOp.type is LLVMNullType) "null" else "$secondOp"}") + + override fun equals(other: Any?) = + (other is LLVMReferenceType) and (typename.equals((other as LLVMReferenceType).typename)) + + override fun hashCode() = + typename.hashCode() +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMShortType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMShortType.kt new file mode 100644 index 00000000000..bb4b07167cf --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMShortType.kt @@ -0,0 +1,45 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + +class LLVMShortType() : LLVMType() { + + override val align = 2 + override var size: Int = 2 + override val defaultValue = "0" + override val typename = "i16" + override val mangle = "Short" + override val isPrimitive = true + + override fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp slt i16 $firstOp, $secondOp") + + override fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sgt i16 $firstOp, $secondOp") + + override fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sle i16 $firstOp, $secondOp") + + override fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp sge i16 $firstOp, $secondOp") + + override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp eq i16" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMBooleanType(), "icmp ne i16" + (if ((firstOp.pointer > 0) || (secondOp.pointer > 0)) "*" else "") + " $firstOp, $secondOp") + + override fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMShortType(), "srem i16 $firstOp, $secondOp") + + override fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue) = + LLVMExpression(LLVMShortType(), "sdiv i16 $firstOp, $secondOp") + + override fun equals(other: Any?) = + other is LLVMShortType + + override fun hashCode() = + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMStringType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMStringType.kt new file mode 100644 index 00000000000..463429c9249 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMStringType.kt @@ -0,0 +1,23 @@ +package org.kotlinnative.translator.llvm.types + +class LLVMStringType(override val length: Int, var isLoaded: Boolean = true) : LLVMArray, LLVMType() { + + override val align = 8 + override var size: Int = 1 + override val defaultValue = "" + override val typename = "i8*" + override val mangle = "String" + override val arrayElementType = LLVMCharType() + override val fullArrayType = "[${length + 1} x i8]" + + override fun equals(other: Any?) = + when (other) { + is LLVMStringType -> this.length == other.length + else -> false + } + + override fun hashCode() = + length * 31 + if (isLoaded) 1 else 0 + + mangle.hashCode() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMType.kt new file mode 100644 index 00000000000..09bb453ae22 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMType.kt @@ -0,0 +1,55 @@ +package org.kotlinnative.translator.llvm.types + +import org.kotlinnative.translator.exceptions.UnimplementedException +import org.kotlinnative.translator.llvm.LLVMExpression +import org.kotlinnative.translator.llvm.LLVMSingleValue + +abstract class LLVMType() { + + companion object { + fun mangleFunctionArguments(names: List) = + mangleFunctionTypes(names.map { it.type }) + + fun mangleFunctionTypes(names: List) = + if (names.size > 0) "_${names.joinToString(separator = "_", transform = { it.mangle })}" else "" + + fun nullOrVoidType(type: LLVMType): Boolean = + (type is LLVMNullType) or (type is LLVMVoidType) + + fun isReferredType(type: LLVMType?): Boolean = + (type is LLVMNullType) or (type is LLVMReferenceType) + + } + + abstract val align: Int + abstract val typename: String + abstract var size: Int + abstract val defaultValue: String + abstract val mangle: String + open val isPrimitive: Boolean = false + + open fun parseArg(inputArg: String) = inputArg + open fun convertFrom(source: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + override fun toString() = typename + + open fun operatorPlus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorTimes(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorMinus(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorDiv(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorLt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorGt(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorLeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorGeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorNeq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorOr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorAnd(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorXor(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorShl(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorShr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorUshr(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorMod(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorInc(firstOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + open fun operatorDec(firstOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() + +} \ No newline at end of file diff --git a/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMVoidType.kt b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMVoidType.kt new file mode 100644 index 00000000000..74d7359ad53 --- /dev/null +++ b/experiments/student-backend/src/org/kotlinnative/translator/llvm/types/LLVMVoidType.kt @@ -0,0 +1,17 @@ +package org.kotlinnative.translator.llvm.types + +class LLVMVoidType() : LLVMType() { + + override val align = 0 + override var size = 0 + override val defaultValue = "" + override val typename = "void" + override val mangle = "" + + override fun equals(other: Any?) = + other is LLVMVoidType + + override fun hashCode() = + typename.hashCode() + +} \ No newline at end of file