[MERGE] Kotlin/Native history merged into kotlin/master

This commit is contained in:
Stanislav Erokhin
2021-02-26 15:30:58 +01:00
3031 changed files with 282024 additions and 32 deletions
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/compiler/ir/backend.native/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="backend" />
<orderEntry type="module" module-name="frontend" />
</component>
</module>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime (3)" level="project" />
<orderEntry type="module" module-name="frontend" />
</component>
</module>
+357
View File
@@ -0,0 +1,357 @@
import org.jetbrains.kotlin.CopyCommonSources
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.*
import org.jetbrains.gradle.plugins.tools.*
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
buildscript {
apply from: "../../kotlin-native/gradle/kotlinGradlePlugin.gradle"
apply plugin: 'project-report'
dependencies {
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.8"
}
}
String protobufVersion = '2.6.1'
apply plugin: "com.google.protobuf"
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: org.jetbrains.kotlin.NativeInteropPlugin
apply plugin: "maven-publish"
sourceSets {
compiler {
proto.srcDir 'compiler/ir/backend.native/src/'
java {
srcDir 'compiler/ir/backend.native/src/'
srcDir 'build/renamed/source/proto/compiler/java'
}
kotlin {
srcDir 'compiler/ir/backend.native/src/'
srcDir '../shared/src/main/kotlin'
srcDir '../shared/src/library/kotlin'
srcDir(VersionGeneratorKt.kotlinNativeVersionSrc(project))
}
resources.srcDir 'compiler/ir/backend.native/resources/'
/* PATH to META-INF */
resources.srcDir VersionGeneratorKt.kotlinNativeVersionResourceFile(project).parentFile.parent
}
cli_bc {
java.srcDir 'cli.bc/src'
kotlin.srcDir 'cli.bc/src'
}
bc_frontend {
java.srcDir 'bc.frontend/src'
kotlin.srcDir 'bc.frontend/src'
}
}
compileCompilerKotlin {
dependsOn('renamePackage')
// The protobuf plugin specifies this dependency for java by itself,
// but not for Kotlin.
dependsOn('generateCompilerProto')
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.allWarningsAsErrors=true
kotlinOptions.freeCompilerArgs += ['-Xopt-in=kotlin.RequiresOptIn', '-Xopt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI', '-Xskip-prerelease-check']
}
compileCli_bcKotlin {
kotlinOptions.freeCompilerArgs += ['-Xskip-prerelease-check']
}
compileCompilerJava {
dependsOn('renamePackage')
}
task copyGenerated(type: Copy) {
dependsOn('generateCompilerProto')
from 'build/generated/source/proto/compiler/java'
into 'build/renamed/source/proto/compiler/java'
filter { line -> line.replaceAll("com.google.protobuf", "org.jetbrains.kotlin.protobuf") }
outputs.dir('build/renamed')
}
task deleteGenerated(type: Delete) {
dependsOn('copyGenerated')
delete 'build/generated/source/proto/compiler/java'
}
task renamePackage {
dependsOn('copyGenerated', 'deleteGenerated')
}
kotlinNativeInterop {
llvm {
dependsOn ":kotlin-native:llvmDebugInfoC:${NativePluginKt.lib("debugInfo")}"
dependsOn ":kotlin-native:llvmCoverageMappingC:${NativePluginKt.lib("coverageMapping")}"
defFile 'llvm.def'
if (!project.parent.convention.plugins.platformInfo.isWindows())
compilerOpts "-fPIC"
compilerOpts "-I$llvmDir/include", "-I${rootProject.project(':kotlin-native:llvmDebugInfoC').projectDir}/src/main/include", "-I${rootProject.project(':kotlin-native:llvmCoverageMappingC').projectDir}/src/main/include"
linkerOpts "-L$llvmDir/lib", "-L${rootProject.project(':kotlin-native:llvmDebugInfoC').buildDir}", "-L${rootProject.project(':kotlin-native:llvmCoverageMappingC').buildDir}"
}
hash { // TODO: copy-pasted from ':common:compileHash'
if (!rootProject.project(":kotlin-native").convention.plugins.platformInfo.isWindows()) {
compilerOpts '-fPIC'
linkerOpts '-fPIC'
}
linker 'clang++'
linkOutputs ":kotlin-native:common:${hostName}Hash"
headers fileTree('../common/src/hash/headers') {
include '**/*.h'
include '**/*.hpp'
}
pkg 'org.jetbrains.kotlin.backend.konan.hash'
}
files {
if (!project.project(":kotlin-native").convention.plugins.platformInfo.isWindows()) {
compilerOpts '-fPIC'
linkerOpts '-fPIC'
}
linker 'clang++'
linkOutputs ":kotlin-native:common:${hostName}Files"
headers fileTree('../common/src/files/headers') {
include '**/*.h'
include '**/*.hpp'
}
pkg 'org.jetbrains.kotlin.backend.konan.files'
}
}
configurations {
kotlin_compiler_jar
kotlin_stdlib_jar
kotlin_reflect_jar
kotlin_script_runtime_jar
trove4j_jar
kotlinCommonSources
cli_bcRuntime {
extendsFrom compilerRuntime
extendsFrom kotlin_script_runtime_jar
}
cli_bc {
extendsFrom cli_bcRuntime
}
cli_bcCompile.extendsFrom compilerCompile
}
dependencies {
trove4j_jar "org.jetbrains.intellij.deps:trove4j:1.0.20181211@jar"
kotlin_compiler_jar project(kotlinCompilerModule)
kotlin_stdlib_jar kotlinStdLibModule
kotlin_reflect_jar kotlinReflectModule
kotlin_script_runtime_jar project(":kotlin-script-runtime")
[kotlinCommonStdlibModule, kotlinTestCommonModule, kotlinTestAnnotationsCommonModule].each {
kotlinCommonSources(it) { transitive = false }
}
compilerCompile project(":kotlin-native:utilities:basic-utils")
compilerCompile "com.google.protobuf:protobuf-java:${protobufVersion}"
compilerCompile project(kotlinCompilerModule)
compilerCompile project(":native:kotlin-native-utils")
compilerCompile project(":core:descriptors")
compilerCompile project(":compiler:ir.tree")
compilerCompile project(":compiler:ir.tree.impl")
compilerCompile project(":compiler:ir.backend.common")
compilerCompile project(":compiler:util")
compilerCompile project(":native:frontend.native")
compilerCompile project(":compiler:cli-common")
compilerCompile project(":compiler:cli")
compilerCompile project(":kotlin-util-klib")
compilerCompile project(":kotlin-util-klib-metadata")
compilerCompile project(":compiler:ir.serialization.common")
use(LocalDependenciesKt) {
compilerCompile(intellijCoreDep()){
artifact {
name = "intellij-core"
type = "jar"
}
}
compilerCompile(intellijDep()){
artifact {
name = "util"
type = "jar"
//extention = "jar"
}
}
compileOnly(jpsStandalone())
}
compilerCompile kotlinNativeInterop['llvm'].configuration
compilerCompile kotlinNativeInterop['hash'].configuration
compilerCompile kotlinNativeInterop['files'].configuration
cli_bcCompile sourceSets.compiler.output
bc_frontendCompile project(kotlinCompilerModule)
cli_bc sourceSets.cli_bc.output
}
classes.dependsOn 'compilerClasses', 'cli_bcClasses', 'bc_frontendClasses'
// These are just a couple of aliases
task stdlib(dependsOn: "${hostName}Stdlib")
def commonSrc = project(":kotlin-stdlib-common").files("src/kotlin", "src/generated", "../unsigned/src", "../src").files
final List<File> stdLibSrc = [
project(':kotlin-native:Interop:Runtime').file('src/main/kotlin'),
project(':kotlin-native:Interop:Runtime').file('src/native/kotlin'),
project(':kotlin-native:Interop:JsRuntime').file('src/main/kotlin'),
project(':kotlin-native:runtime').file('src/main/kotlin')
]
// These files are built before the 'dist' is complete,
// so we provide a custom value for --runtime
targetList.each { target ->
def konanJvmArgs = [*HostManager.regularJvmArgs]
def defaultArgs = ['-nopack', '-nostdlib', '-no-default-libs', '-no-endorsed-libs',
//Uncomment this '-Werror' when common stdlib will be ready
]
if (target != "wasm32") defaultArgs += '-g'
def konanArgs = [*defaultArgs,
'-target', target,
"-Xruntime=${project(':kotlin-native:runtime').file('build/bitcode/main/' + target + '/runtime.bc')}",
*project.globalBuildArgs]
task("${target}Stdlib", type: JavaExec) {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
// This task depends on distCompiler, so the compiler jar is already in the dist directory.
classpath = fileTree("${UtilsKt.getKotlinNativeDist(project)}/konan/lib") {
include "*.jar"
}
systemProperties "konan.home": UtilsKt.getKotlinNativeDist(project)
jvmArgs = konanJvmArgs
def testAnnotationCommon = project(":kotlin-test:kotlin-test-annotations-common").files("src/main/kotlin").files
def testCommon = project(":kotlin-test:kotlin-test-common").files("src/main/kotlin").files
args = [*konanArgs,
'-output', project(':kotlin-native:runtime').file("build/${target}Stdlib"),
'-produce', 'library', '-module-name', 'stdlib', '-XXLanguage:+AllowContractsForCustomFunctions',
'-Xmulti-platform', '-Xopt-in=kotlin.RequiresOptIn', '-Xinline-classes',
'-Xopt-in=kotlin.contracts.ExperimentalContracts',
'-Xopt-in=kotlin.ExperimentalMultiplatform',
'-Xallow-result-return-type',
*commonSrc.toList(),
*testAnnotationCommon.toList(),
*testCommon.toList(),
"-Xcommon-sources=${commonSrc.join(',')}",
"-Xcommon-sources=${testAnnotationCommon.join(',')}",
"-Xcommon-sources=${testCommon.join(',')}",
*stdLibSrc]
stdLibSrc.forEach { inputs.dir(it) }
commonSrc.forEach{
inputs.dir(it)
}
outputs.dir(project(':kotlin-native:runtime').file("build/${target}Stdlib"))
dependsOn ":kotlin-native:runtime:${target}Runtime"
dependsOn ":kotlin-native:distCompiler"
}
}
task run {
doLast {
logger.quiet("Run the outer project 'demo' target to compile the test source.")
}
}
jar {
from sourceSets.cli_bc.output,
sourceSets.compiler.output,
sourceSets.hashInteropStubs.output,
sourceSets.filesInteropStubs.output,
sourceSets.llvmInteropStubs.output
dependsOn ':kotlin-native:runtime:hostRuntime', 'external_jars'
}
def externalJars = ['compiler', 'stdlib', 'reflect', 'script_runtime']
task trove4jCopy(type: Copy) {
from configurations.getByName("trove4j_jar") {
include "trove4j*.jar"
rename "trove4j(.*).jar", "trove4j.jar"
into 'build/external_jars'
}
}
externalJars.each { arg ->
def jar = arg.replace('_', '-') // :(
task("${arg}Copy", type: Copy) {
from configurations.getByName("kotlin_${arg}_jar") {
include "kotlin-${jar}*.jar"
rename "kotlin-${jar}(.*).jar", "kotlin-${jar}.jar"
into 'build/external_jars'
}
}
}
task external_jars(type: Copy) {
dependsOn externalJars.collect { "${it}Copy" }
dependsOn trove4jCopy
from configurations.compilerCompile {
include "protobuf-java-${protobufVersion}.jar"
into 'build/external_jars'
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
}
task debugCompiler(type: JavaExec) {
dependsOn ':dist'
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.fileTree("${distDir.canonicalPath}/konan/lib/") {
include '*.jar'
}
jvmArgs "-Dorg.jetbrains.kotlin.native.home=${distDir.canonicalPath}"
enableAssertions = true
args = findProperty("konan.debug.args").toString().tokenize() ?: []
}
publishing {
repositories {
maven { url = "$buildDir/repo" }
}
publications {
maven(MavenPublication) {
groupId = 'org.jetbrains.kotlin'
artifactId = 'backend.native'
version = konanVersionFull
from components.java
}
}
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Kotlin" />
<orderEntry type="module" module-name="cli" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="ir.psi2ir" />
<orderEntry type="module" module-name="ir.tree" />
<orderEntry type="module" module-name="frontend.java" />
</component>
</module>
@@ -0,0 +1,485 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.cli.bc
import com.intellij.openapi.Disposable
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.util.profile
import org.jetbrains.kotlin.utils.KotlinPaths
private class K2NativeCompilerPerformanceManager: CommonCompilerPerformanceManager("Kotlin to Native Compiler")
class K2Native : CLICompiler<K2NativeCompilerArguments>() {
override fun MutableList<String>.addPlatformOptions(arguments: K2NativeCompilerArguments) {}
override fun createMetadataVersion(versionArray: IntArray): BinaryVersion = KlibMetadataVersion(*versionArray)
override val performanceManager:CommonCompilerPerformanceManager by lazy {
K2NativeCompilerPerformanceManager()
}
override fun doExecute(@NotNull arguments: K2NativeCompilerArguments,
@NotNull configuration: CompilerConfiguration,
@NotNull rootDisposable: Disposable,
@Nullable paths: KotlinPaths?): ExitCode {
if (arguments.version) {
println("Kotlin/Native: ${CompilerVersion.CURRENT}")
return ExitCode.OK
}
val pluginLoadResult =
PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, configuration)
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable,
configuration, EnvironmentConfigFiles.NATIVE_CONFIG_FILES)
val project = environment.project
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) ?: MessageCollector.NONE
configuration.put(CLIConfigurationKeys.PHASE_CONFIG, createPhaseConfig(toplevelPhase, arguments, messageCollector))
val konanConfig = KonanConfig(project, configuration)
val enoughArguments = arguments.freeArgs.isNotEmpty() || arguments.isUsefulWithoutFreeArgs
if (!enoughArguments) {
configuration.report(ERROR, "You have not specified any compilation arguments. No output has been produced.")
}
/* Set default version of metadata version */
val metadataVersionString = arguments.metadataVersion
if (metadataVersionString == null) {
configuration.put(CommonConfigurationKeys.METADATA_VERSION, KlibMetadataVersion.INSTANCE)
}
try {
runTopLevelPhases(konanConfig, environment)
} catch (e: KonanCompilationException) {
return ExitCode.COMPILATION_ERROR
} catch (e: Throwable) {
configuration.report(ERROR, """
|Compilation failed: ${e.message}
| * Source files: ${environment.getSourceFiles().joinToString(transform = KtFile::getName)}
| * Compiler version info: Konan: ${CompilerVersion.CURRENT} / Kotlin: ${KotlinVersion.CURRENT}
| * Output kind: ${configuration.get(KonanConfigKeys.PRODUCE)}
""".trimMargin())
throw e
}
return ExitCode.OK
}
val K2NativeCompilerArguments.isUsefulWithoutFreeArgs: Boolean
get() = listTargets || listPhases || checkDependencies || !includes.isNullOrEmpty() ||
!librariesToCache.isNullOrEmpty() || libraryToAddToCache != null
fun Array<String>?.toNonNullList(): List<String> {
return this?.asList<String>() ?: listOf<String>()
}
// It is executed before doExecute().
override fun setupPlatformSpecificArgumentsAndServices(
configuration: CompilerConfiguration,
arguments : K2NativeCompilerArguments,
services : Services) {
val commonSources = arguments.commonSources?.toSet().orEmpty()
arguments.freeArgs.forEach {
configuration.addKotlinSourceRoot(it, it in commonSources)
}
with(KonanConfigKeys) {
with(configuration) {
arguments.kotlinHome?.let { put(KONAN_HOME, it) }
put(NODEFAULTLIBS, arguments.nodefaultlibs || !arguments.libraryToAddToCache.isNullOrEmpty())
put(NOENDORSEDLIBS, arguments.noendorsedlibs || !arguments.libraryToAddToCache.isNullOrEmpty())
put(NOSTDLIB, arguments.nostdlib || !arguments.libraryToAddToCache.isNullOrEmpty())
put(NOPACK, arguments.nopack)
put(NOMAIN, arguments.nomain)
put(LIBRARY_FILES,
arguments.libraries.toNonNullList())
put(LINKER_ARGS, arguments.linkerArguments.toNonNullList() +
arguments.singleLinkerArguments.toNonNullList())
arguments.moduleName?.let{ put(MODULE_NAME, it) }
arguments.target?.let{ put(TARGET, it) }
put(INCLUDED_BINARY_FILES,
arguments.includeBinaries.toNonNullList())
put(NATIVE_LIBRARY_FILES,
arguments.nativeLibraries.toNonNullList())
put(REPOSITORIES,
arguments.repositories.toNonNullList())
// TODO: Collect all the explicit file names into an object
// and teach the compiler to work with temporaries and -save-temps.
arguments.outputName ?.let { put(OUTPUT, it) }
val outputKind = CompilerOutputKind.valueOf(
(arguments.produce ?: "program").toUpperCase())
put(PRODUCE, outputKind)
put(METADATA_KLIB, arguments.metadataKlib)
arguments.libraryVersion ?. let { put(LIBRARY_VERSION, it) }
arguments.mainPackage ?.let{ put(ENTRY, it) }
arguments.manifestFile ?.let{ put(MANIFEST_FILE, it) }
arguments.runtimeFile ?.let{ put(RUNTIME_FILE, it) }
arguments.temporaryFilesDir?.let { put(TEMPORARY_FILES_DIR, it) }
put(LIST_TARGETS, arguments.listTargets)
put(OPTIMIZATION, arguments.optimization)
put(DEBUG, arguments.debug)
// TODO: remove after 1.4 release.
if (arguments.lightDebugDeprecated) {
configuration.report(WARNING,
"-Xg0 is now deprecated and skipped by compiler. Light debug information is enabled by default for Darwin platforms." +
" For other targets, please, use `-Xadd-light-debug=enable` instead.")
}
putIfNotNull(LIGHT_DEBUG, when (val it = arguments.lightDebugString) {
"enable" -> true
"disable" -> false
null -> null
else -> {
configuration.report(ERROR, "Unsupported -Xadd-light-debug= value: $it. Possible values are 'enable'/'disable'")
null
}
})
put(STATIC_FRAMEWORK, selectFrameworkType(configuration, arguments, outputKind))
put(OVERRIDE_CLANG_OPTIONS, arguments.clangOptions.toNonNullList())
put(ALLOCATION_MODE, arguments.allocator)
put(PRINT_IR, arguments.printIr)
put(PRINT_IR_WITH_DESCRIPTORS, arguments.printIrWithDescriptors)
put(PRINT_DESCRIPTORS, arguments.printDescriptors)
put(PRINT_LOCATIONS, arguments.printLocations)
put(PRINT_BITCODE, arguments.printBitCode)
put(PURGE_USER_LIBS, arguments.purgeUserLibs)
if (arguments.verifyCompiler != null)
put(VERIFY_COMPILER, arguments.verifyCompiler == "true")
put(VERIFY_IR, arguments.verifyIr)
put(VERIFY_BITCODE, arguments.verifyBitCode)
put(ENABLED_PHASES,
arguments.enablePhases.toNonNullList())
put(DISABLED_PHASES,
arguments.disablePhases.toNonNullList())
put(LIST_PHASES, arguments.listPhases)
put(ENABLE_ASSERTIONS, arguments.enableAssertions)
put(MEMORY_MODEL, when (arguments.memoryModel) {
"relaxed" -> {
configuration.report(STRONG_WARNING, "Relaxed memory model is not yet fully functional")
MemoryModel.RELAXED
}
"strict" -> MemoryModel.STRICT
"experimental" -> MemoryModel.EXPERIMENTAL
else -> {
configuration.report(ERROR, "Unsupported memory model ${arguments.memoryModel}")
MemoryModel.STRICT
}
})
when {
arguments.generateWorkerTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.WORKER)
arguments.generateTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD)
arguments.generateNoExitTestRunner -> put(GENERATE_TEST_RUNNER, TestRunnerKind.MAIN_THREAD_NO_EXIT)
else -> put(GENERATE_TEST_RUNNER, TestRunnerKind.NONE)
}
// We need to download dependencies only if we use them ( = there are files to compile).
put(
CHECK_DEPENDENCIES,
configuration.kotlinSourceRoots.isNotEmpty()
|| !arguments.includes.isNullOrEmpty()
|| arguments.checkDependencies
)
if (arguments.friendModules != null)
put(FRIEND_MODULES, arguments.friendModules!!.split(File.pathSeparator).filterNot(String::isEmpty))
put(EXPORTED_LIBRARIES, selectExportedLibraries(configuration, arguments, outputKind))
put(INCLUDED_LIBRARIES, selectIncludes(configuration, arguments, outputKind))
put(FRAMEWORK_IMPORT_HEADERS, arguments.frameworkImportHeaders.toNonNullList())
arguments.emitLazyObjCHeader?.let { put(EMIT_LAZY_OBJC_HEADER_FILE, it) }
put(BITCODE_EMBEDDING_MODE, selectBitcodeEmbeddingMode(this, arguments))
put(DEBUG_INFO_VERSION, arguments.debugInfoFormatVersion.toInt())
put(COVERAGE, arguments.coverage)
put(LIBRARIES_TO_COVER, arguments.coveredLibraries.toNonNullList())
arguments.coverageFile?.let { put(PROFRAW_PATH, it) }
put(OBJC_GENERICS, !arguments.noObjcGenerics)
put(DEBUG_PREFIX_MAP, parseDebugPrefixMap(arguments, configuration))
put(LIBRARIES_TO_CACHE, parseLibrariesToCache(arguments, configuration, outputKind))
val libraryToAddToCache = parseLibraryToAddToCache(arguments, configuration, outputKind)
if (libraryToAddToCache != null && !arguments.outputName.isNullOrEmpty())
configuration.report(ERROR, "$ADD_CACHE already implicitly sets output file name")
val cacheDirectories = arguments.cacheDirectories.toNonNullList()
libraryToAddToCache?.let { put(LIBRARY_TO_ADD_TO_CACHE, it) }
put(CACHE_DIRECTORIES, cacheDirectories)
put(CACHED_LIBRARIES, parseCachedLibraries(arguments, configuration))
parseShortModuleName(arguments, configuration, outputKind)?.let {
put(SHORT_MODULE_NAME, it)
}
put(FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator)
putIfNotNull(PRE_LINK_CACHES, parsePreLinkCachesValue(configuration, arguments.preLinkCaches))
putIfNotNull(OVERRIDE_KONAN_PROPERTIES, parseOverrideKonanProperties(arguments, configuration))
put(DESTROY_RUNTIME_MODE, when (arguments.destroyRuntimeMode) {
"legacy" -> DestroyRuntimeMode.LEGACY
"on-shutdown" -> DestroyRuntimeMode.ON_SHUTDOWN
else -> {
configuration.report(ERROR, "Unsupported destroy runtime mode ${arguments.destroyRuntimeMode}")
DestroyRuntimeMode.ON_SHUTDOWN
}
})
}
}
}
override fun createArguments() = K2NativeCompilerArguments()
override fun executableScriptFileName() = "kotlinc-native"
companion object {
@JvmStatic fun main(args: Array<String>) {
profile("Total compiler main()") {
doMain(K2Native(), args)
}
}
@JvmStatic fun mainNoExit(args: Array<String>) {
profile("Total compiler main()") {
if (doMainNoExit(K2Native(), args) != ExitCode.OK) {
throw KonanCompilationException("Compilation finished with errors")
}
}
}
@JvmStatic fun mainNoExitWithGradleRenderer(args: Array<String>) {
profile("Total compiler main()") {
if (doMainNoExit(K2Native(), args, MessageRenderer.GRADLE_STYLE) != ExitCode.OK) {
throw KonanCompilationException("Compilation finished with errors")
}
}
}
}
}
private fun selectFrameworkType(
configuration: CompilerConfiguration,
arguments: K2NativeCompilerArguments,
outputKind: CompilerOutputKind
): Boolean {
return if (outputKind != CompilerOutputKind.FRAMEWORK && arguments.staticFramework) {
configuration.report(
STRONG_WARNING,
"'$STATIC_FRAMEWORK_FLAG' is only supported when producing frameworks, " +
"but the compiler is producing ${outputKind.name.toLowerCase()}"
)
false
} else {
arguments.staticFramework
}
}
private fun parsePreLinkCachesValue(
configuration: CompilerConfiguration,
value: String?
): Boolean? = when (value) {
"enable" -> true
"disable" -> false
null -> null
else -> {
configuration.report(ERROR, "Unsupported `-Xpre-link-caches` value: $value. Possible values are 'enable'/'disable'")
null
}
}
private fun selectBitcodeEmbeddingMode(
configuration: CompilerConfiguration,
arguments: K2NativeCompilerArguments
): BitcodeEmbedding.Mode = when {
arguments.embedBitcodeMarker -> {
if (arguments.embedBitcode) {
configuration.report(
STRONG_WARNING,
"'$EMBED_BITCODE_FLAG' is ignored because '$EMBED_BITCODE_MARKER_FLAG' is specified"
)
}
BitcodeEmbedding.Mode.MARKER
}
arguments.embedBitcode -> {
BitcodeEmbedding.Mode.FULL
}
else -> BitcodeEmbedding.Mode.NONE
}
private fun selectExportedLibraries(
configuration: CompilerConfiguration,
arguments: K2NativeCompilerArguments,
outputKind: CompilerOutputKind
): List<String> {
val exportedLibraries = arguments.exportedLibraries?.toList().orEmpty()
return if (exportedLibraries.isNotEmpty() && outputKind != CompilerOutputKind.FRAMEWORK &&
outputKind != CompilerOutputKind.STATIC && outputKind != CompilerOutputKind.DYNAMIC) {
configuration.report(STRONG_WARNING,
"-Xexport-library is only supported when producing frameworks or native libraries, " +
"but the compiler is producing ${outputKind.name.toLowerCase()}")
emptyList()
} else {
exportedLibraries
}
}
private fun selectIncludes(
configuration: CompilerConfiguration,
arguments: K2NativeCompilerArguments,
outputKind: CompilerOutputKind
): List<String> {
val includes = arguments.includes?.toList().orEmpty()
return if (includes.isNotEmpty() && outputKind == CompilerOutputKind.LIBRARY) {
configuration.report(
ERROR,
"The $INCLUDE_ARG flag is not supported when producing ${outputKind.name.toLowerCase()}"
)
emptyList()
} else {
includes
}
}
private fun parseCachedLibraries(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration
): Map<String, String> = arguments.cachedLibraries?.asList().orEmpty().mapNotNull {
val libraryAndCache = it.split(",")
if (libraryAndCache.size != 2) {
configuration.report(
ERROR,
"incorrect $CACHED_LIBRARY format: expected '<library>,<cache>', got '$it'"
)
null
} else {
libraryAndCache[0] to libraryAndCache[1]
}
}.toMap()
private fun parseLibrariesToCache(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration,
outputKind: CompilerOutputKind
): List<String> {
val input = arguments.librariesToCache?.asList().orEmpty()
return if (input.isNotEmpty() && !outputKind.isCache) {
configuration.report(ERROR, "$MAKE_CACHE can't be used when not producing cache")
emptyList()
} else if (input.isNotEmpty() && !arguments.libraryToAddToCache.isNullOrEmpty()) {
configuration.report(ERROR, "supplied both $MAKE_CACHE and $ADD_CACHE options")
emptyList()
} else {
input
}
}
private fun parseLibraryToAddToCache(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration,
outputKind: CompilerOutputKind
): String? {
val input = arguments.libraryToAddToCache
return if (input != null && !outputKind.isCache) {
configuration.report(ERROR, "$ADD_CACHE can't be used when not producing cache")
null
} else {
input
}
}
// TODO: Support short names for current module in ObjC export and lift this limitation.
private fun parseShortModuleName(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration,
outputKind: CompilerOutputKind
): String? {
val input = arguments.shortModuleName
return if (input != null && outputKind != CompilerOutputKind.LIBRARY) {
configuration.report(
STRONG_WARNING,
"$SHORT_MODULE_NAME_ARG is only supported when producing a Kotlin library, " +
"but the compiler is producing ${outputKind.name.toLowerCase()}"
)
null
} else {
input
}
}
private fun parseDebugPrefixMap(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration
): Map<String, String> = arguments.debugPrefixMap?.asList().orEmpty().mapNotNull {
val libraryAndCache = it.split("=")
if (libraryAndCache.size != 2) {
configuration.report(
ERROR,
"incorrect debug prefix map format: expected '<old>=<new>', got '$it'"
)
null
} else {
libraryAndCache[0] to libraryAndCache[1]
}
}.toMap()
private fun parseOverrideKonanProperties(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration
): Map<String, String>? =
arguments.overrideKonanProperties?.mapNotNull {
val keyValueSeparatorIndex = it.indexOf('=')
if (keyValueSeparatorIndex > 0) {
it.substringBefore('=') to it.substringAfter('=')
} else {
configuration.report(
ERROR,
"incorrect property format: expected '<key>=<value>', got '$it'"
)
null
}
}?.toMap()
fun main(args: Array<String>) = K2Native.main(args)
fun mainNoExitWithGradleRenderer(args: Array<String>) = K2Native.mainNoExitWithGradleRenderer(args)
@@ -0,0 +1,321 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.cli.bc
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.*
class K2NativeCompilerArguments : CommonCompilerArguments() {
// First go the options interesting to the general public.
// Prepend them with a single dash.
// Keep the list lexically sorted.
@Argument(value = "-enable-assertions", deprecatedName = "-enable_assertions", shortName = "-ea", description = "Enable runtime assertions in generated code")
var enableAssertions: Boolean = false
@Argument(value = "-g", description = "Enable emitting debug information")
var debug: Boolean = false
@Argument(value = "-generate-test-runner", deprecatedName = "-generate_test_runner",
shortName = "-tr", description = "Produce a runner for unit tests")
var generateTestRunner = false
@Argument(value = "-generate-worker-test-runner",
shortName = "-trw", description = "Produce a worker runner for unit tests")
var generateWorkerTestRunner = false
@Argument(value = "-generate-no-exit-test-runner",
shortName = "-trn", description = "Produce a runner for unit tests not forcing exit")
var generateNoExitTestRunner = false
@Argument(value="-include-binary", deprecatedName = "-includeBinary", shortName = "-ib", valueDescription = "<path>", description = "Pack external binary within the klib")
var includeBinaries: Array<String>? = null
@Argument(value = "-library", shortName = "-l", valueDescription = "<path>", description = "Link with the library", delimiter = "")
var libraries: Array<String>? = null
@Argument(value = "-library-version", shortName = "-lv", valueDescription = "<version>", description = "Set library version")
var libraryVersion: String? = null
@Argument(value = "-list-targets", deprecatedName = "-list_targets", description = "List available hardware targets")
var listTargets: Boolean = false
@Argument(value = "-manifest", valueDescription = "<path>", description = "Provide a maniferst addend file")
var manifestFile: String? = null
@Argument(value="-memory-model", valueDescription = "<model>", description = "Memory model to use, 'strict', 'relaxed' and 'experimental' are currently supported")
var memoryModel: String? = "strict"
@Argument(value="-module-name", deprecatedName = "-module_name", valueDescription = "<name>", description = "Specify a name for the compilation module")
var moduleName: String? = null
@Argument(value = "-native-library", deprecatedName = "-nativelibrary", shortName = "-nl",
valueDescription = "<path>", description = "Include the native bitcode library", delimiter = "")
var nativeLibraries: Array<String>? = null
@Argument(value = "-no-default-libs", deprecatedName = "-nodefaultlibs", description = "Don't link the libraries from dist/klib automatically")
var nodefaultlibs: Boolean = false
@Argument(value = "-no-endorsed-libs", description = "Don't link the endorsed libraries from dist automatically")
var noendorsedlibs: Boolean = false
@Argument(value = "-nomain", description = "Assume 'main' entry point to be provided by external libraries")
var nomain: Boolean = false
@Argument(value = "-nopack", description = "Don't pack the library into a klib file")
var nopack: Boolean = false
@Argument(value="-linker-options", deprecatedName = "-linkerOpts", valueDescription = "<arg>", description = "Pass arguments to linker", delimiter = " ")
var linkerArguments: Array<String>? = null
@Argument(value="-linker-option", valueDescription = "<arg>", description = "Pass argument to linker", delimiter = "")
var singleLinkerArguments: Array<String>? = null
@Argument(value = "-nostdlib", description = "Don't link with stdlib")
var nostdlib: Boolean = false
@Argument(value = "-opt", description = "Enable optimizations during compilation")
var optimization: Boolean = false
@Argument(value = "-output", shortName = "-o", valueDescription = "<name>", description = "Output name")
var outputName: String? = null
@Argument(value = "-entry", shortName = "-e", valueDescription = "<name>", description = "Qualified entry point name")
var mainPackage: String? = null
@Argument(value = "-produce", shortName = "-p",
valueDescription = "{program|static|dynamic|framework|library|bitcode}",
description = "Specify output file kind")
var produce: String? = null
@Argument(value = "-repo", shortName = "-r", valueDescription = "<path>", description = "Library search path")
var repositories: Array<String>? = null
@Argument(value = "-target", valueDescription = "<target>", description = "Set hardware target")
var target: String? = null
// The rest of the options are only interesting to the developers.
// Make sure to prepend them with -X.
// Keep the list lexically sorted.
@Argument(
value = "-Xcache-directory",
valueDescription = "<path>",
description = "Path to the directory containing caches",
delimiter = ""
)
var cacheDirectories: Array<String>? = null
@Argument(
value = CACHED_LIBRARY,
valueDescription = "<library path>,<cache path>",
description = "Comma-separated paths of a library and its cache",
delimiter = ""
)
var cachedLibraries: Array<String>? = null
@Argument(value="-Xcheck-dependencies", deprecatedName = "--check_dependencies", description = "Check dependencies and download the missing ones")
var checkDependencies: Boolean = false
@Argument(value = EMBED_BITCODE_FLAG, description = "Embed LLVM IR bitcode as data")
var embedBitcode: Boolean = false
@Argument(value = EMBED_BITCODE_MARKER_FLAG, description = "Embed placeholder LLVM IR data as a marker")
var embedBitcodeMarker: Boolean = false
@Argument(value = "-Xemit-lazy-objc-header", description = "")
var emitLazyObjCHeader: String? = null
@Argument(value = "-Xenable", deprecatedName = "--enable", valueDescription = "<Phase>", description = "Enable backend phase")
var enablePhases: Array<String>? = null
@Argument(
value = "-Xexport-library",
valueDescription = "<path>",
description = "A library to be included into produced framework API.\n" +
"Must be one of libraries passed with '-library'",
delimiter = ""
)
var exportedLibraries: Array<String>? = null
@Argument(value="-Xfake-override-validator", description = "Enable IR fake override validator")
var fakeOverrideValidator: Boolean = false
@Argument(
value = "-Xframework-import-header",
valueDescription = "<header>",
description = "Add additional header import to framework header"
)
var frameworkImportHeaders: Array<String>? = null
@Argument(
value = "-Xadd-light-debug",
valueDescription = "{disable|enable}",
description = "Add light debug information for optimized builds. This option is skipped in debug builds.\n" +
"It's enabled by default on Darwin platforms where collected debug information is stored in .dSYM file.\n" +
"Currently option is disabled by default on other platforms."
)
var lightDebugString: String? = null
// TODO: remove after 1.4 release.
@Argument(value = "-Xg0", description = "Add light debug information. Deprecated option. Please use instead -Xadd-light-debug=enable")
var lightDebugDeprecated: Boolean = false
@Argument(
value = MAKE_CACHE,
valueDescription = "<path>",
description = "Path of the library to be compiled to cache",
delimiter = ""
)
var librariesToCache: Array<String>? = null
@Argument(
value = ADD_CACHE,
valueDescription = "<path>",
description = "Path to the library to be added to cache",
delimiter = ""
)
var libraryToAddToCache: String? = null
@Argument(value = "-Xprint-bitcode", deprecatedName = "--print_bitcode", description = "Print llvm bitcode")
var printBitCode: Boolean = false
@Argument(value = "-Xprint-descriptors", deprecatedName = "--print_descriptors", description = "Print descriptor tree")
var printDescriptors: Boolean = false
@Argument(value = "-Xprint-ir", deprecatedName = "--print_ir", description = "Print IR")
var printIr: Boolean = false
@Argument(value = "-Xprint-ir-with-descriptors", deprecatedName = "--print_ir_with_descriptors", description = "Print IR with descriptors")
var printIrWithDescriptors: Boolean = false
@Argument(value = "-Xprint-locations", deprecatedName = "--print_locations", description = "Print locations")
var printLocations: Boolean = false
@Argument(value="-Xpurge-user-libs", deprecatedName = "--purge_user_libs", description = "Don't link unused libraries even explicitly specified")
var purgeUserLibs: Boolean = false
@Argument(value = "-Xruntime", deprecatedName = "--runtime", valueDescription = "<path>", description = "Override standard 'runtime.bc' location")
var runtimeFile: String? = null
@Argument(
value = INCLUDE_ARG,
valueDescription = "<path>",
description = "A path to an intermediate library that should be processed in the same manner as source files"
)
var includes: Array<String>? = null
@Argument(
value = SHORT_MODULE_NAME_ARG,
valueDescription = "<name>",
description = "A short name used to denote this library in the IDE and in a generated Objective-C header"
)
var shortModuleName: String? = null
@Argument(value = STATIC_FRAMEWORK_FLAG, description = "Create a framework with a static library instead of a dynamic one")
var staticFramework: Boolean = false
@Argument(value = "-Xtemporary-files-dir", deprecatedName = "--temporary_files_dir", valueDescription = "<path>", description = "Save temporary files to the given directory")
var temporaryFilesDir: String? = null
@Argument(value = "-Xverify-bitcode", deprecatedName = "--verify_bitcode", description = "Verify llvm bitcode after each method")
var verifyBitCode: Boolean = false
@Argument(value = "-Xverify-ir", description = "Verify IR")
var verifyIr: Boolean = false
@Argument(value = "-Xverify-compiler", description = "Verify compiler")
var verifyCompiler: String? = null
@Argument(
value = "-friend-modules",
valueDescription = "<path>",
description = "Paths to friend modules"
)
var friendModules: String? = null
@Argument(value = "-Xdebug-info-version", description = "generate debug info of given version (1, 2)")
var debugInfoFormatVersion: String = "1" /* command line parser doesn't accept kotlin.Int type */
@Argument(value = "-Xcoverage", description = "emit coverage")
var coverage: Boolean = false
@Argument(
value = "-Xlibrary-to-cover",
valueDescription = "<path>",
description = "Provide code coverage for the given library.\n" +
"Must be one of libraries passed with '-library'",
delimiter = ""
)
var coveredLibraries: Array<String>? = null
@Argument(value = "-Xcoverage-file", valueDescription = "<path>", description = "Save coverage information to the given file")
var coverageFile: String? = null
@Argument(value = "-Xno-objc-generics", description = "Disable generics support for framework header")
var noObjcGenerics: Boolean = false
@Argument(value="-Xoverride-clang-options", valueDescription = "<arg1,arg2,...>", description = "Explicit list of Clang options")
var clangOptions: Array<String>? = null
@Argument(value="-Xallocator", valueDescription = "std | mimalloc", description = "Allocator used in runtime")
var allocator: String = "std"
@Argument(value = "-Xmetadata-klib", description = "Produce a klib that only contains the declarations metadata")
var metadataKlib: Boolean = false
@Argument(value = "-Xdebug-prefix-map", valueDescription = "<old1=new1,old2=new2,...>", description = "Remap file source directory paths in debug info")
var debugPrefixMap: Array<String>? = null
@Argument(
value = "-Xpre-link-caches",
valueDescription = "{disable|enable}",
description = "Perform caches pre-link"
)
var preLinkCaches: String? = null
// We use `;` as delimiter because properties may contain comma-separated values.
// For example, target cpu features.
@Argument(
value = "-Xoverride-konan-properties",
valueDescription = "key1=value1;key2=value2;...",
description = "Override konan.properties.values",
delimiter = ";"
)
var overrideKonanProperties: Array<String>? = null
@Argument(value="-Xdestroy-runtime-mode", valueDescription = "<mode>", description = "When to destroy runtime. 'legacy' and 'on-shutdown' are currently supported. NOTE: 'legacy' mode is deprecated and will be removed.")
var destroyRuntimeMode: String? = "on-shutdown"
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> =
super.configureAnalysisFlags(collector).also {
val useExperimental = it[AnalysisFlags.useExperimental] as List<*>
it[AnalysisFlags.useExperimental] = useExperimental + listOf("kotlin.ExperimentalUnsignedTypes")
if (printIr)
phasesToDumpAfter = arrayOf("ALL")
}
override fun checkIrSupport(languageVersionSettings: LanguageVersionSettings, collector: MessageCollector) {
if (languageVersionSettings.languageVersion < LanguageVersion.KOTLIN_1_4
|| languageVersionSettings.apiVersion < ApiVersion.KOTLIN_1_4
) {
collector.report(
severity = CompilerMessageSeverity.ERROR,
message = "Native backend cannot be used with language or API version below 1.4"
)
}
}
}
const val EMBED_BITCODE_FLAG = "-Xembed-bitcode"
const val EMBED_BITCODE_MARKER_FLAG = "-Xembed-bitcode-marker"
const val STATIC_FRAMEWORK_FLAG = "-Xstatic-framework"
const val INCLUDE_ARG = "-Xinclude"
const val CACHED_LIBRARY = "-Xcached-library"
const val MAKE_CACHE = "-Xmake-cache"
const val ADD_CACHE = "-Xadd-cache"
const val SHORT_MODULE_NAME_ARG = "-Xshort-module-name"
@@ -0,0 +1 @@
org.jetbrains.kotlin.backend.konan.ObjCOverridabilityCondition
@@ -0,0 +1,272 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* Transforms expressions depending on the context they are used in.
*
* The transformations are defined with `IrExpression.use*` methods in this class,
* the most common are [useAs], [useAsStatement], [useInTypeOperator].
*
* TODO: the implementation is originally based on [org.jetbrains.kotlin.psi2ir.transformations.InsertImplicitCasts]
* and should probably be used as its base.
*
* TODO: consider making this visitor non-recursive to make it more general.
*/
internal abstract class AbstractValueUsageTransformer(
val builtIns: KotlinBuiltIns,
val symbols: KonanSymbols,
val irBuiltIns: IrBuiltIns
): IrElementTransformerVoid() {
protected open fun IrExpression.useAs(type: IrType): IrExpression = this
protected open fun IrExpression.useAsStatement(): IrExpression = this
protected open fun IrExpression.useInTypeOperator(operator: IrTypeOperator, typeOperand: IrType): IrExpression =
this
protected open fun IrExpression.useAsValue(value: IrValueDeclaration): IrExpression = this.useAs(value.type)
protected open fun IrExpression.useAsArgument(parameter: IrValueParameter): IrExpression =
this.useAsValue(parameter)
protected open fun IrExpression.useAsDispatchReceiver(expression: IrFunctionAccessExpression): IrExpression =
this.useAsArgument(expression.symbol.owner.dispatchReceiverParameter!!)
protected open fun IrExpression.useAsExtensionReceiver(expression: IrFunctionAccessExpression): IrExpression =
this.useAsArgument(expression.symbol.owner.extensionReceiverParameter!!)
protected open fun IrExpression.useAsValueArgument(expression: IrFunctionAccessExpression,
parameter: IrValueParameter
): IrExpression =
this.useAsArgument(parameter)
private fun IrExpression.useForVariable(variable: IrVariable): IrExpression =
this.useAsValue(variable)
private fun IrExpression.useForValue(value: IrValueDeclaration) =
this.useAsValue(value)
private fun IrExpression.useForField(field: IrField): IrExpression =
this.useAs(field.type)
protected open fun IrExpression.useAsReturnValue(returnTarget: IrReturnTargetSymbol): IrExpression =
when (returnTarget) {
is IrSimpleFunctionSymbol -> this.useAs(returnTarget.owner.returnType)
is IrConstructorSymbol -> this.useAs(irBuiltIns.unitType)
is IrReturnableBlockSymbol -> this.useAs(returnTarget.owner.type)
else -> error(returnTarget)
}
protected open fun IrExpression.useAsResult(enclosing: IrExpression): IrExpression =
this.useAs(enclosing.type)
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
TODO()
}
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
TODO()
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
TODO()
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
expression.transformChildrenVoid(this)
with(expression) {
dispatchReceiver = dispatchReceiver?.useAsDispatchReceiver(expression)
extensionReceiver = extensionReceiver?.useAsExtensionReceiver(expression)
for (index in symbol.owner.valueParameters.indices) {
val argument = getValueArgument(index) ?: continue
val parameter = symbol.owner.valueParameters[index]
putValueArgument(index, argument.useAsValueArgument(expression, parameter))
}
}
return expression
}
override fun visitBlockBody(body: IrBlockBody): IrBody {
body.transformChildrenVoid(this)
body.statements.forEachIndexed { i, irStatement ->
if (irStatement is IrExpression) {
body.statements[i] = irStatement.useAsStatement()
}
}
return body
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
expression.transformChildrenVoid(this)
if (expression.statements.isEmpty()) {
return expression
}
val lastIndex = expression.statements.lastIndex
expression.statements.forEachIndexed { i, irStatement ->
if (irStatement is IrExpression) {
expression.statements[i] =
if (i == lastIndex)
irStatement.useAsResult(expression)
else
irStatement.useAsStatement()
}
}
return expression
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
expression.value = expression.value.useAsReturnValue(expression.returnTargetSymbol)
return expression
}
override fun visitSetValue(expression: IrSetValue): IrExpression {
expression.transformChildrenVoid(this)
expression.value = expression.value.useForValue(expression.symbol.owner)
return expression
}
override fun visitSetField(expression: IrSetField): IrExpression {
expression.transformChildrenVoid(this)
expression.value = expression.value.useForField(expression.symbol.owner)
return expression
}
override fun visitField(declaration: IrField): IrStatement {
declaration.transformChildrenVoid(this)
declaration.initializer?.let {
it.expression = it.expression.useForField(declaration)
}
return declaration
}
override fun visitVariable(declaration: IrVariable): IrVariable {
declaration.transformChildrenVoid(this)
declaration.initializer = declaration.initializer?.useForVariable(declaration)
return declaration
}
override fun visitWhen(expression: IrWhen): IrExpression {
expression.transformChildrenVoid(this)
for (irBranch in expression.branches) {
irBranch.condition = irBranch.condition.useAs(irBuiltIns.booleanType)
irBranch.result = irBranch.result.useAsResult(expression)
}
return expression
}
override fun visitLoop(loop: IrLoop): IrExpression {
loop.transformChildrenVoid(this)
loop.condition = loop.condition.useAs(irBuiltIns.booleanType)
loop.body = loop.body?.useAsStatement()
return loop
}
override fun visitThrow(expression: IrThrow): IrExpression {
expression.transformChildrenVoid(this)
expression.value = expression.value.useAs(symbols.throwable.owner.defaultType)
return expression
}
override fun visitTry(aTry: IrTry): IrExpression {
aTry.transformChildrenVoid(this)
aTry.tryResult = aTry.tryResult.useAsResult(aTry)
for (aCatch in aTry.catches) {
aCatch.result = aCatch.result.useAsResult(aTry)
}
aTry.finallyExpression = aTry.finallyExpression?.useAsStatement()
return aTry
}
override fun visitVararg(expression: IrVararg): IrExpression {
expression.transformChildrenVoid(this)
expression.elements.forEachIndexed { i, element ->
when (element) {
is IrSpreadElement ->
element.expression = element.expression.useAs(expression.type)
is IrExpression ->
expression.putElement(i, element.useAs(expression.varargElementType))
}
}
return expression
}
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
expression.transformChildrenVoid(this)
expression.argument = expression.argument.useInTypeOperator(expression.operator, expression.typeOperand)
return expression
}
override fun visitFunction(declaration: IrFunction): IrStatement {
declaration.transformChildrenVoid(this)
declaration.valueParameters.forEach { parameter ->
val defaultValue = parameter.defaultValue
if (defaultValue is IrExpressionBody) {
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
}
}
declaration.body?.let {
if (it is IrExpressionBody) {
it.expression = it.expression.useAsReturnValue(declaration.symbol)
}
}
return declaration
}
// TODO: IrStringConcatenation, IrEnumEntry?
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
sealed class BinaryType<out T> {
class Primitive(val type: PrimitiveBinaryType) : BinaryType<Nothing>()
class Reference<T>(val types: Sequence<T>, val nullable: Boolean) : BinaryType<T>()
}
fun BinaryType<*>.primitiveBinaryTypeOrNull(): PrimitiveBinaryType? = when (this) {
is BinaryType.Primitive -> this.type
is BinaryType.Reference -> null
}
enum class PrimitiveBinaryType {
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, POINTER, VECTOR128
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.target.*
typealias BitcodeFile = String
typealias ObjectFile = String
typealias ExecutableFile = String
internal class BitcodeCompiler(val context: Context) {
private val platform = context.config.platform
private val optimize = context.shouldOptimize()
private val debug = context.config.debug
private val overrideClangOptions =
context.configuration.getList(KonanConfigKeys.OVERRIDE_CLANG_OPTIONS)
private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { it.isNotEmpty() })
}
private fun runTool(vararg command: String) =
Command(*command)
.logWith(context::log)
.execute()
private fun temporary(name: String, suffix: String): String =
context.config.tempFiles.create(name, suffix).absolutePath
private fun targetTool(tool: String, vararg arg: String) {
val absoluteToolName = if (platform.configurables is AppleConfigurables) {
"${platform.absoluteTargetToolchain}/usr/bin/$tool"
} else {
"${platform.absoluteTargetToolchain}/bin/$tool"
}
runTool(absoluteToolName, *arg)
}
private fun hostLlvmTool(tool: String, vararg arg: String) {
val absoluteToolName = "${platform.absoluteLlvmHome}/bin/$tool"
runTool(absoluteToolName, *arg)
}
private fun clang(configurables: ClangFlags, file: BitcodeFile): ObjectFile {
val objectFile = temporary("result", ".o")
val profilingFlags = llvmProfilingFlags().map { listOf("-mllvm", it) }.flatten()
// TODO: fix with LLVM update.
val targetTriple = when (context.config.target) {
// LLVM we use does not have support for arm64_32.
KonanTarget.WATCHOS_ARM64 -> {
require(configurables is AppleConfigurables)
"arm64_32-apple-watchos${configurables.osVersionMin}"
}
// Runtime generates bitcode for mythical macos 10.16 because of old Clang.
// Let's fix it.
KonanTarget.MACOS_ARM64 -> {
require(configurables is AppleConfigurables)
"arm64-apple-macos${configurables.osVersionMin}"
}
else -> context.llvm.targetTriple
}
val flags = overrideClangOptions.takeIf(List<String>::isNotEmpty)
?: mutableListOf<String>().apply {
addNonEmpty(configurables.clangFlags)
addNonEmpty(listOf("-triple", targetTriple))
if (configurables is ZephyrConfigurables) {
addNonEmpty(configurables.constructClangCC1Args())
}
addNonEmpty(when {
optimize -> configurables.clangOptFlags
debug -> configurables.clangDebugFlags
else -> configurables.clangNooptFlags
})
addNonEmpty(BitcodeEmbedding.getClangOptions(context.config))
addNonEmpty(configurables.currentRelocationMode(context).translateToClangCc1Flag())
addNonEmpty(profilingFlags)
}
if (configurables is AppleConfigurables) {
targetTool("clang++", *flags.toTypedArray(), file, "-o", objectFile)
} else {
hostLlvmTool("clang++", *flags.toTypedArray(), file, "-o", objectFile)
}
return objectFile
}
private fun RelocationModeFlags.Mode.translateToClangCc1Flag() = when (this) {
RelocationModeFlags.Mode.PIC -> listOf("-mrelocation-model", "pic")
RelocationModeFlags.Mode.STATIC -> listOf("-mrelocation-model", "static")
RelocationModeFlags.Mode.DEFAULT -> emptyList()
}
private fun llvmProfilingFlags(): List<String> {
val flags = mutableListOf<String>()
if (context.shouldProfilePhases()) {
flags += "-time-passes"
}
if (context.inVerbosePhase) {
flags += "-debug-pass=Structure"
}
return flags
}
fun makeObjectFiles(bitcodeFile: BitcodeFile): List<ObjectFile> =
listOf(when (val configurables = platform.configurables) {
is ClangFlags -> clang(configurables, bitcodeFile)
else -> error("Unsupported configurables kind: ${configurables::class.simpleName}!")
})
}
@@ -0,0 +1,22 @@
package org.jetbrains.kotlin.backend.konan
object BitcodeEmbedding {
enum class Mode {
NONE, MARKER, FULL
}
internal fun getLinkerOptions(config: KonanConfig): List<String> = when (config.bitcodeEmbeddingMode) {
Mode.NONE -> emptyList()
Mode.MARKER -> listOf("-bitcode_bundle", "-bitcode_process_mode", "marker")
Mode.FULL -> listOf("-bitcode_bundle")
}
internal fun getClangOptions(config: KonanConfig): List<String> = when (config.bitcodeEmbeddingMode) {
Mode.NONE -> listOf("-fembed-bitcode=off")
Mode.MARKER -> listOf("-fembed-bitcode=marker")
Mode.FULL -> listOf("-fembed-bitcode=all")
}
private val KonanConfig.bitcodeEmbeddingMode get() = configuration.get(KonanConfigKeys.BITCODE_EMBEDDING_MODE)!!
}
@@ -0,0 +1,221 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import llvm.*
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
internal fun KonanSymbols.getTypeConversion(actualType: IrType, expectedType: IrType): IrSimpleFunctionSymbol? =
getTypeConversionImpl(actualType.getInlinedClassNative(), expectedType.getInlinedClassNative())
private fun KonanSymbols.getTypeConversionImpl(
actualInlinedClass: IrClass?,
expectedInlinedClass: IrClass?
): IrSimpleFunctionSymbol? {
if (actualInlinedClass == expectedInlinedClass) return null
return when {
actualInlinedClass == null && expectedInlinedClass == null -> null
actualInlinedClass != null && expectedInlinedClass == null -> context.getBoxFunction(actualInlinedClass)
actualInlinedClass == null && expectedInlinedClass != null -> context.getUnboxFunction(expectedInlinedClass)
else -> error("actual type is ${actualInlinedClass?.fqNameForIrSerialization}, expected ${expectedInlinedClass?.fqNameForIrSerialization}")
}?.symbol
}
internal object DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION : IrDeclarationOriginImpl("INLINE_CLASS_SPECIAL_FUNCTION")
internal val Context.getBoxFunction: (IrClass) -> IrSimpleFunction by Context.lazyMapMember { inlinedClass ->
assert(inlinedClass.isUsedAsBoxClass())
assert(inlinedClass.parent is IrFile) { "Expected top level inline class" }
val symbols = ir.symbols
val isNullable = inlinedClass.inlinedClassIsNullable()
val unboxedType = inlinedClass.defaultOrNullableType(isNullable)
val boxedType = symbols.any.owner.defaultOrNullableType(isNullable)
val parameterType = unboxedType
val returnType = boxedType
val startOffset = inlinedClass.startOffset
val endOffset = inlinedClass.endOffset
IrFunctionImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION,
IrSimpleFunctionSymbolImpl(),
Name.special("<${inlinedClass.name}-box>"),
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
returnType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).also { function ->
function.valueParameters = listOf(
IrValueParameterImpl(
startOffset, endOffset,
IrDeclarationOrigin.DEFINED,
IrValueParameterSymbolImpl(),
Name.identifier("value"),
index = 0,
varargElementType = null,
isCrossinline = false,
type = parameterType,
isNoinline = false,
isHidden = false,
isAssignable = false
).apply {
parent = function
})
function.parent = inlinedClass.getContainingFile()!!
}
}
internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context.lazyMapMember { inlinedClass ->
assert(inlinedClass.isUsedAsBoxClass())
assert(inlinedClass.parent is IrFile) { "Expected top level inline class" }
val symbols = ir.symbols
val isNullable = inlinedClass.inlinedClassIsNullable()
val unboxedType = inlinedClass.defaultOrNullableType(isNullable)
val boxedType = symbols.any.owner.defaultOrNullableType(isNullable)
val parameterType = boxedType
val returnType = unboxedType
val startOffset = inlinedClass.startOffset
val endOffset = inlinedClass.endOffset
IrFunctionImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION,
IrSimpleFunctionSymbolImpl(),
Name.special("<${inlinedClass.name}-unbox>"),
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
returnType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).also { function ->
function.valueParameters = listOf(
IrValueParameterImpl(
startOffset, endOffset,
IrDeclarationOrigin.DEFINED,
IrValueParameterSymbolImpl(),
Name.identifier("value"),
index = 0,
varargElementType = null,
isCrossinline = false,
type = parameterType,
isNoinline = false,
isHidden = false,
isAssignable = false
).apply {
parent = function
})
function.parent = inlinedClass.getContainingFile()!!
}
}
/**
* Initialize static boxing.
* If output target is native binary then the cache is created.
*/
internal fun initializeCachedBoxes(context: Context) {
if (context.producedLlvmModuleContainsStdlib) {
BoxCache.values().forEach { cache ->
val cacheName = "${cache.name}_CACHE"
val rangeStart = "${cache.name}_RANGE_FROM"
val rangeEnd = "${cache.name}_RANGE_TO"
initCache(cache, context, cacheName, rangeStart, rangeEnd)
}
}
}
/**
* Adds global that refers to the cache.
*/
private fun initCache(cache: BoxCache, context: Context, cacheName: String,
rangeStartName: String, rangeEndName: String) {
val kotlinType = context.irBuiltIns.getKotlinClass(cache)
val staticData = context.llvm.staticData
val llvmType = staticData.getLLVMType(kotlinType.defaultType)
val (start, end) = context.config.target.getBoxCacheRange(cache)
// Constancy of these globals allows LLVM's constant propagation and DCE
// to remove fast path of boxing function in case of empty range.
staticData.placeGlobal(rangeStartName, createConstant(llvmType, start), true)
.setConstant(true)
staticData.placeGlobal(rangeEndName, createConstant(llvmType, end), true)
.setConstant(true)
val values = (start..end).map { staticData.createInitializer(kotlinType, createConstant(llvmType, it)) }
val llvmBoxType = structType(context.llvm.runtime.objHeaderType, llvmType)
staticData.placeGlobalConstArray(cacheName, llvmBoxType, values, true).llvm
}
private fun createConstant(llvmType: LLVMTypeRef, value: Int): ConstValue =
constValue(LLVMConstInt(llvmType, value.toLong(), 1)!!)
// When start is greater than end then `inRange` check is always false
// and can be eliminated by LLVM.
private val emptyRange = 1 to 0
// Memory usage is around 20kb.
private val BoxCache.defaultRange get() = when (this) {
BoxCache.BOOLEAN -> (0 to 1)
BoxCache.BYTE -> (-128 to 127)
BoxCache.SHORT -> (-128 to 127)
BoxCache.CHAR -> (0 to 255)
BoxCache.INT -> (-128 to 127)
BoxCache.LONG -> (-128 to 127)
}
private fun KonanTarget.getBoxCacheRange(cache: BoxCache): Pair<Int, Int> = when (this) {
is KonanTarget.ZEPHYR -> emptyRange
else -> cache.defaultRange
}
internal fun IrBuiltIns.getKotlinClass(cache: BoxCache): IrClass = when (cache) {
BoxCache.BOOLEAN -> booleanClass
BoxCache.BYTE -> byteClass
BoxCache.SHORT -> shortClass
BoxCache.CHAR -> charClass
BoxCache.INT -> intClass
BoxCache.LONG -> longClass
}.owner
// TODO: consider adding box caches for unsigned types.
enum class BoxCache {
BOOLEAN, BYTE, SHORT, CHAR, INT, LONG
}
@@ -0,0 +1,354 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.functions.FunctionClassKind
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.descriptors.IrAbstractFunctionFactory
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.impl.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.OperatorNameConventions
internal object DECLARATION_ORIGIN_FUNCTION_CLASS : IrDeclarationOriginImpl("DECLARATION_ORIGIN_FUNCTION_CLASS")
internal class BuiltInFictitiousFunctionIrClassFactory(
var symbolTable: SymbolTable?,
private val irBuiltIns: IrBuiltIns,
private val reflectionTypes: KonanReflectionTypes
) : IrAbstractFunctionFactory(), IrProvider {
override fun getDeclaration(symbol: IrSymbol) =
(symbol.descriptor as? FunctionClassDescriptor)?.let { descriptor ->
buildClass(descriptor) {
declareClass(descriptor) {
createIrClass(it, descriptor)
}
}
}
var module: IrModuleFragment? = null
set(value) {
if (value == null)
error("Provide a valid non-null module")
if (field != null)
error("Module has already been set")
field = value
value.files += filesMap.values
// builtClasses.forEach { it.addFakeOverrides() }
}
class FunctionalInterface(val irClass: IrClass, val descriptor: FunctionClassDescriptor, val arity: Int)
fun buildAllClasses() {
val maxArity = 255 // See [BuiltInFictitiousFunctionClassFactory].
(0 .. maxArity).forEach { arity ->
functionN(arity)
kFunctionN(arity)
suspendFunctionN(arity)
kSuspendFunctionN(arity)
}
}
override fun functionClassDescriptor(arity: Int): FunctionClassDescriptor =
irBuiltIns.builtIns.getFunction(arity) as FunctionClassDescriptor
override fun kFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
reflectionTypes.getKFunction(arity) as FunctionClassDescriptor
override fun suspendFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
irBuiltIns.builtIns.getSuspendFunction(arity) as FunctionClassDescriptor
override fun kSuspendFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
reflectionTypes.getKSuspendFunction(arity) as FunctionClassDescriptor
override fun functionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(irBuiltIns.builtIns.getFunction(arity) as FunctionClassDescriptor, declarator)
override fun kFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(reflectionTypes.getKFunction(arity) as FunctionClassDescriptor, declarator)
override fun suspendFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(irBuiltIns.builtIns.getSuspendFunction(arity) as FunctionClassDescriptor, declarator)
override fun kSuspendFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(reflectionTypes.getKSuspendFunction(arity) as FunctionClassDescriptor, declarator)
private val functionSymbol = symbolTable!!.referenceClass(
irBuiltIns.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.function))!!)
private val kFunctionSymbol = symbolTable!!.referenceClass(
irBuiltIns.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.kFunction))!!)
private val filesMap = mutableMapOf<PackageFragmentDescriptor, IrFile>()
private val builtClassesMap = mutableMapOf<FunctionClassDescriptor, IrClass>()
val builtClasses get() = builtClassesMap.values
val builtFunctionNClasses get() = builtClassesMap.entries.mapNotNull { (descriptor, irClass) ->
with(descriptor) {
if (functionKind == FunctionClassKind.Function)
FunctionalInterface(irClass, descriptor, arity)
else null
}
}
private fun createTypeParameter(descriptor: TypeParameterDescriptor): IrTypeParameter =
symbolTable?.declareGlobalTypeParameter(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, DECLARATION_ORIGIN_FUNCTION_CLASS,
descriptor
)
?: IrTypeParameterImpl(
SYNTHETIC_OFFSET,
SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_FUNCTION_CLASS,
IrTypeParameterSymbolImpl(descriptor),
descriptor.name,
descriptor.index,
descriptor.isReified,
descriptor.variance
)
private fun createSimpleFunction(
descriptor: FunctionDescriptor,
origin: IrDeclarationOrigin,
returnType: IrType
): IrSimpleFunction {
val functionFactory: (IrSimpleFunctionSymbol) -> IrSimpleFunction = {
with(descriptor) {
IrFunctionImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, origin, it, name, visibility, modality, returnType,
isInline, isExternal, isTailrec, isSuspend, isOperator, isInfix, isExpect
)
}
}
return symbolTable?.declareSimpleFunction(descriptor, functionFactory)
?: functionFactory(IrSimpleFunctionSymbolImpl(descriptor))
}
private fun createIrClass(symbol: IrClassSymbol, descriptor: ClassDescriptor): IrClass =
IrFactoryImpl.createIrClassFromDescriptor(offset, offset, DECLARATION_ORIGIN_FUNCTION_CLASS, symbol, descriptor)
private fun createClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
symbolTable?.declarator { createIrClass(it, descriptor) }
?: createIrClass(IrClassSymbolImpl(descriptor), descriptor)
private fun buildClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
builtClassesMap.getOrPut(descriptor) {
createClass(descriptor, declarator).apply {
val functionClass = this
typeParameters += descriptor.declaredTypeParameters.map { typeParameterDescriptor ->
createTypeParameter(typeParameterDescriptor).also {
it.parent = this
it.superTypes += irBuiltIns.anyNType
}
}
val descriptorToIrParametersMap = typeParameters.map { it.descriptor to it }.toMap()
superTypes += descriptor.typeConstructor.supertypes.map { superType ->
val arguments = superType.arguments.map { argument ->
val argumentClassifierDescriptor = argument.type.constructor.declarationDescriptor
val argumentClassifierSymbol = argumentClassifierDescriptor?.let { descriptorToIrParametersMap[it] }
?: error("Unexpected super type argument: $argumentClassifierDescriptor")
makeTypeProjection(argumentClassifierSymbol.defaultType, argument.projectionKind)
}
val superTypeSymbol = when (val superTypeDescriptor = superType.constructor.declarationDescriptor) {
is FunctionClassDescriptor -> buildClass(superTypeDescriptor) {
declareClass(superTypeDescriptor) {
createIrClass(it, superTypeDescriptor)
}
}.symbol
functionSymbol.descriptor -> functionSymbol
kFunctionSymbol.descriptor -> kFunctionSymbol
else -> error("Unexpected super type: $superTypeDescriptor")
}
IrSimpleTypeImpl(superTypeSymbol, superType.isMarkedNullable, arguments, emptyList())
}
createParameterDeclarations()
val invokeFunctionDescriptor = descriptor.unsubstitutedMemberScope.getContributedFunctions(
OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND).single()
val isFakeOverride = invokeFunctionDescriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
if (!isFakeOverride) {
val invokeFunctionOrigin =
if (isFakeOverride)
IrDeclarationOrigin.FAKE_OVERRIDE
else
DECLARATION_ORIGIN_FUNCTION_CLASS
declarations += createSimpleFunction(
invokeFunctionDescriptor, invokeFunctionOrigin,
typeParameters.last().defaultType
).apply {
parent = functionClass
valueParameters += invokeFunctionDescriptor.valueParameters.map {
IrValueParameterImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, invokeFunctionOrigin,
IrValueParameterSymbolImpl(it), it.name, it.index,
functionClass.typeParameters[it.index].defaultType, null,
it.isCrossinline, it.isNoinline,
isHidden = false, isAssignable = false
).also { it.parent = this }
}
if (!isFakeOverride)
createDispatchReceiverParameter(invokeFunctionOrigin)
else {
val overriddenFunction = superTypes
.mapNotNull { it.classOrNull?.owner }
.single { it.descriptor is FunctionClassDescriptor }
.simpleFunctions()
.single { it.name == OperatorNameConventions.INVOKE }
overriddenSymbols += overriddenFunction.symbol
dispatchReceiverParameter = overriddenFunction.dispatchReceiverParameter?.copyTo(this)
}
}
}
// Unfortunately, addFakeOverrides() uses some parents but they are only set after PsiToIr phase.
// So we add all the fake overrides only when we're supplied with the module (this is done after PsiToIr).
// if (this@BuiltInFictitiousFunctionIrClassFactory.module != null)
addFakeOverrides()
val packageFragmentDescriptor = descriptor.findPackage()
val file = filesMap.getOrPut(packageFragmentDescriptor) {
IrFileImpl(NaiveSourceBasedFileEntryImpl("[K][Suspend]Functions"), packageFragmentDescriptor).also {
this@BuiltInFictitiousFunctionIrClassFactory.module?.files?.add(it)
}
}
parent = file
file.declarations += this
}
}
private fun toIrType(wrapped: KotlinType): IrType {
val kotlinType = wrapped.unwrap()
return with(IrSimpleTypeBuilder()) {
classifier =
symbolTable?.referenceClassifier(kotlinType.constructor.declarationDescriptor ?: error("No classifier for type $kotlinType"))
hasQuestionMark = kotlinType.isMarkedNullable
arguments = kotlinType.arguments.map {
if (it.isStarProjection) IrStarProjectionImpl
else makeTypeProjection(toIrType(it.type), it.projectionKind)
}
buildSimpleType()
}
}
private fun IrFunction.createValueParameter(descriptor: ParameterDescriptor): IrValueParameter {
val varargType = if (descriptor is ValueParameterDescriptor) descriptor.varargElementType else null
return IrValueParameterImpl(
offset,
offset,
memberOrigin,
IrValueParameterSymbolImpl(descriptor),
descriptor.name,
descriptor.indexOrMinusOne,
toIrType(descriptor.type),
varargType?.let { toIrType(it) },
descriptor.isCrossinline,
descriptor.isNoinline,
isHidden = false,
isAssignable = false
).also {
it.parent = this
}
}
private fun IrClass.addFakeOverrides() {
val fakeOverrideDescriptors = descriptor.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.CALLABLES)
.filterIsInstance<CallableMemberDescriptor>().filter { it.kind === CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
fun createFakeOverrideFunction(descriptor: FunctionDescriptor, property: IrPropertySymbol?): IrSimpleFunction {
val returnType = descriptor.returnType?.let { toIrType(it) } ?: error("No return type for $descriptor")
val functionDeclare = { s: IrSimpleFunctionSymbol ->
descriptor.run {
IrFunctionImpl(
offset, offset, memberOrigin, s, name, visibility, modality, returnType,
isInline, isExternal, isTailrec, isSuspend, isOperator, isInfix, isExpect,
isFakeOverride = true
)
}
}
val newFunction = symbolTable?.declareSimpleFunction(descriptor, functionDeclare)
?: functionDeclare(IrSimpleFunctionSymbolImpl(descriptor))
newFunction.parent = this
newFunction.overriddenSymbols = descriptor.overriddenDescriptors.mapNotNull { symbolTable?.referenceSimpleFunction(it.original) }
newFunction.dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { newFunction.createValueParameter(it) }
newFunction.extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { newFunction.createValueParameter(it) }
newFunction.valueParameters = descriptor.valueParameters.map { newFunction.createValueParameter(it) }
newFunction.correspondingPropertySymbol = property
return newFunction
}
fun createFakeOverrideProperty(descriptor: PropertyDescriptor): IrProperty {
val propertyDeclare = { s: IrPropertySymbol ->
IrPropertyImpl(
startOffset = offset,
endOffset = offset,
origin = memberOrigin,
symbol = s,
name = descriptor.name,
visibility = descriptor.visibility,
modality = descriptor.modality,
isVar = descriptor.isVar,
isConst = descriptor.isConst,
isLateinit = descriptor.isLateInit,
isDelegated = descriptor.isDelegated,
isExternal = descriptor.isExternal,
isExpect = descriptor.isExpect,
isFakeOverride = memberOrigin == IrDeclarationOrigin.FAKE_OVERRIDE)
}
val property = symbolTable?.declareProperty(offset, offset, memberOrigin, descriptor, propertyFactory = propertyDeclare)
?: propertyDeclare(IrPropertySymbolImpl(descriptor))
property.parent = this
property.getter = descriptor.getter?.let { g -> createFakeOverrideFunction(g, property.symbol) }
property.setter = descriptor.setter?.let { s -> createFakeOverrideFunction(s, property.symbol) }
return property
}
fun createFakeOverride(descriptor: CallableMemberDescriptor): IrDeclaration {
return when (descriptor) {
is FunctionDescriptor -> createFakeOverrideFunction(descriptor, null)
is PropertyDescriptor -> createFakeOverrideProperty(descriptor)
else -> error("Unexpected member $descriptor")
}
}
declarations += fakeOverrideDescriptors.map { createFakeOverride(it) }
}
}
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.konan.exec.*
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.file.*
fun produceCAdapterBitcode(clang: ClangArgs, cppFileName: String, bitcodeFileName: String) {
val clangCommand = clang.clangCXX("-std=c++17", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName)
Command(clangCommand).execute()
}
@@ -0,0 +1,94 @@
package org.jetbrains.kotlin.backend.konan
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.konan.exec.Command
import org.jetbrains.kotlin.konan.file.*
import org.jetbrains.kotlin.konan.target.ClangArgs
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
class CStubsManager(private val target: KonanTarget) {
fun getUniqueName(prefix: String) = "$prefix${counter++}"
fun addStub(kotlinLocation: CompilerMessageLocation?, lines: List<String>) {
stubs += Stub(kotlinLocation, lines)
}
fun compile(clang: ClangArgs, messageCollector: MessageCollector, verbose: Boolean): File? {
if (stubs.isEmpty()) return null
val compilerOptions = mutableListOf<String>()
val sourceFileExtension = when {
target.family.isAppleFamily -> {
compilerOptions += "-fobjc-arc"
".m" // TODO: consider managing C and Objective-C stubs separately.
}
else -> ".c"
}
val cSource = createTempFile("cstubs", sourceFileExtension).deleteOnExit()
cSource.writeLines(stubs.flatMap { it.lines })
val bitcode = createTempFile("cstubs", ".bc").deleteOnExit()
val cSourcePath = cSource.absolutePath
val clangCommand = clang.clangC(*compilerOptions.toTypedArray(), "-O2",
cSourcePath, "-emit-llvm", "-c", "-o", bitcode.absolutePath)
val result = Command(clangCommand).getResult(withErrors = true)
if (result.exitCode != 0) {
reportCompilationErrors(cSourcePath, result, messageCollector, verbose)
}
return bitcode
}
private fun reportCompilationErrors(
cSourcePath: String,
result: Command.Result,
messageCollector: MessageCollector,
verbose: Boolean
): Nothing {
val regex = Regex("${Regex.escape(cSourcePath)}:([0-9]+):[0-9]+: error: .*")
val errorLines = result.outputLines.mapNotNull { line ->
regex.matchEntire(line)?.let { matchResult ->
matchResult.groupValues[1].toInt()
}
}
val lineToStub = ArrayList<Stub>()
stubs.forEach { stub ->
repeat(stub.lines.size) { lineToStub.add(stub) }
}
val cSourceCopyPath = "cstubs.c"
if (verbose) {
File(cSourcePath).copyTo(File(cSourceCopyPath))
}
if (errorLines.isNotEmpty()) {
errorLines.forEach {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Unable to compile C bridge" + if (verbose) " at $cSourceCopyPath:$it" else "",
lineToStub[it - 1].kotlinLocation
)
}
} else {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Unable to compile C bridges",
null
)
}
throw KonanCompilationException()
}
private val stubs = mutableListOf<Stub>()
private class Stub(val kotlinLocation: CompilerMessageLocation?, val lines: List<String>)
private var counter = 0
}
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
class CacheSupport(
val configuration: CompilerConfiguration,
resolvedLibraries: KotlinLibraryResolveResult,
target: KonanTarget,
produce: CompilerOutputKind
) {
private val allLibraries = resolvedLibraries.getFullList()
// TODO: consider using [FeaturedLibraries.kt].
private val fileToLibrary = allLibraries.associateBy { it.libraryFile }
private val implicitCacheDirectories = configuration.get(KonanConfigKeys.CACHE_DIRECTORIES)!!
.map {
File(it).takeIf { it.isDirectory }
?: configuration.reportCompilationError("cache directory $it is not found or not a directory")
}
internal fun tryGetImplicitOutput(): String? {
val libraryToAddToCache = configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE) ?: return null
// Put the resulting library in the first cache directory.
val cacheDirectory = implicitCacheDirectories.firstOrNull() ?: return null
val libraryToAddToCacheFile = File(libraryToAddToCache)
val library = allLibraries.single { it.libraryFile == libraryToAddToCacheFile }
return cacheDirectory.child(CachedLibraries.getCachedLibraryName(library)).absolutePath
}
internal val cachedLibraries: CachedLibraries = run {
val explicitCacheFiles = configuration.get(KonanConfigKeys.CACHED_LIBRARIES)!!
val explicitCaches = explicitCacheFiles.entries.associate { (libraryPath, cachePath) ->
val library = fileToLibrary[File(libraryPath)]
?: configuration.reportCompilationError("cache not applied: library $libraryPath in $cachePath")
library to cachePath
}
val optimized = configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)
if (optimized && (explicitCacheFiles.isNotEmpty() || implicitCacheDirectories.isNotEmpty()))
configuration.report(CompilerMessageSeverity.WARNING, "Cached libraries will not be used for optimized compilation")
CachedLibraries(
target = target,
allLibraries = allLibraries,
explicitCaches = if (optimized) emptyMap() else explicitCaches,
implicitCacheDirectories = if (optimized) emptyList() else implicitCacheDirectories
)
}
private fun getLibrary(file: File) =
fileToLibrary[file] ?: error("library to cache\n" +
" ${file.absolutePath}\n" +
"not found among resolved libraries:\n " +
allLibraries.joinToString("\n ") { it.libraryFile.absolutePath })
internal val librariesToCache: Set<KotlinLibrary> = run {
val libraryToAddToCachePath = configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE)
if (libraryToAddToCachePath.isNullOrEmpty()) {
configuration.get(KonanConfigKeys.LIBRARIES_TO_CACHE)!!
.map { getLibrary(File(it)) }
.toSet()
.also { if (!produce.isCache) check(it.isEmpty()) }
} else {
val libraryToAddToCacheFile = File(libraryToAddToCachePath)
val libraryToAddToCache = getLibrary(libraryToAddToCacheFile)
val libraryCache = cachedLibraries.getLibraryCache(libraryToAddToCache)
if (libraryCache == null)
setOf(libraryToAddToCache)
else
emptySet()
}
}
internal val preLinkCaches: Boolean =
configuration.get(KonanConfigKeys.PRE_LINK_CACHES, false)
init {
// Ensure dependencies of every cached library are cached too:
resolvedLibraries.getFullList { libraries ->
libraries.map { library ->
val cache = cachedLibraries.getLibraryCache(library.library)
if (cache != null || library.library in librariesToCache) {
library.resolvedDependencies.forEach {
if (!cachedLibraries.isLibraryCached(it.library) && it.library !in librariesToCache) {
val description = if (cache != null) {
"cached (in ${cache.path})"
} else {
"going to be cached"
}
configuration.reportCompilationError(
"${library.library.libraryName} is $description, " +
"but its dependency isn't: ${it.library.libraryName}"
)
}
}
}
library
}
}
// Ensure not making cache for libraries that are already cached:
librariesToCache.forEach {
val cache = cachedLibraries.getLibraryCache(it)
if (cache != null) {
configuration.reportCompilationError("Can't cache library '${it.libraryName}' " +
"that is already cached in '${cache.path}'")
}
}
if ((librariesToCache.isNotEmpty() || cachedLibraries.hasDynamicCaches || cachedLibraries.hasStaticCaches)
&& configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)) {
configuration.reportCompilationError("Cache cannot be used in optimized compilation")
}
}
}
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class CachedLibraries(
private val target: KonanTarget,
allLibraries: List<KotlinLibrary>,
explicitCaches: Map<KotlinLibrary, String>,
implicitCacheDirectories: List<File>
) {
class Cache(val kind: Kind, val path: String) {
enum class Kind { DYNAMIC, STATIC }
val bitcodeDependencies by lazy {
val directory = File(path).absoluteFile.parent
File(directory, BITCODE_DEPENDENCIES_FILE_NAME).readStrings()
}
}
private val cacheDirsContents = mutableMapOf<String, Set<String>>()
private fun selectCache(library: KotlinLibrary, cacheDir: File): Cache? {
// See Linker.renameOutput why is it ok to have an empty cache directory.
val cacheDirContents = cacheDirsContents.getOrPut(cacheDir.absolutePath) {
cacheDir.listFilesOrEmpty.map { it.absolutePath }.toSet()
}
if (cacheDirContents.isEmpty()) return null
val baseName = getCachedLibraryName(library)
val dynamicFile = cacheDir.child(getArtifactName(baseName, CompilerOutputKind.DYNAMIC_CACHE))
val staticFile = cacheDir.child(getArtifactName(baseName, CompilerOutputKind.STATIC_CACHE))
if (dynamicFile.absolutePath in cacheDirContents && staticFile.absolutePath in cacheDirContents)
error("Both dynamic and static caches files cannot be in the same directory." +
" Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}")
return when {
dynamicFile.absolutePath in cacheDirContents -> Cache(Cache.Kind.DYNAMIC, dynamicFile.absolutePath)
staticFile.absolutePath in cacheDirContents -> Cache(Cache.Kind.STATIC, staticFile.absolutePath)
else -> error("No cache found for library ${library.libraryName} at ${cacheDir.absolutePath}")
}
}
private val allCaches: Map<KotlinLibrary, Cache> = allLibraries.mapNotNull { library ->
val explicitPath = explicitCaches[library]
val cache = if (explicitPath != null) {
selectCache(library, File(explicitPath))
?: error("No cache found for library ${library.libraryName} at $explicitPath")
} else {
implicitCacheDirectories.firstNotNullResult { dir ->
selectCache(library, dir.child(getCachedLibraryName(library)))
}
}
cache?.let { library to it }
}.toMap()
private fun getArtifactName(baseName: String, kind: CompilerOutputKind) =
"${kind.prefix(target)}$baseName${kind.suffix(target)}"
fun isLibraryCached(library: KotlinLibrary): Boolean =
getLibraryCache(library) != null
fun getLibraryCache(library: KotlinLibrary): Cache? =
allCaches[library]
val hasStaticCaches = allCaches.values.any {
when (it.kind) {
Cache.Kind.STATIC -> true
Cache.Kind.DYNAMIC -> false
}
}
val hasDynamicCaches = allCaches.values.any {
when (it.kind) {
Cache.Kind.STATIC -> false
Cache.Kind.DYNAMIC -> true
}
}
companion object {
fun getCachedLibraryName(library: KotlinLibrary): String = getCachedLibraryName(library.uniqueName)
fun getCachedLibraryName(libraryName: String): String = "$libraryName-cache"
const val BITCODE_DEPENDENCIES_FILE_NAME = "bitcode_deps"
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName
internal fun IrClass.isNonGeneratedAnnotation(): Boolean =
this.kind == ClassKind.ANNOTATION_CLASS &&
!this.annotations.hasAnnotation(serialInfoAnnotationFqName)
private val serialInfoAnnotationFqName = FqName("kotlinx.serialization.SerialInfo")
/**
* We don't need to generate RTTI in some cases, e.g. Objective-C external classes.
*/
internal fun IrClass.requiresRtti(): Boolean = when {
// TODO: Support more cases
// Sadly, we still need to emit RTTI for Kotlin inheritors of Obj-C classes.
// The reason for it is that we need to know a layout of the object to correctly
// deinitialize it.
this.isExternalObjCClass() -> false
else -> true
}
internal fun IrClass.requiresCodeGeneration(): Boolean =
// For now these two sets (classes that require RTTI and classes that require codegen)
// are the same, but they might diverge later.
requiresRtti()
@@ -0,0 +1,211 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import llvm.*
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.llvm.objc.linkObjC
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.file.isBitcode
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.library.impl.buildLibrary
/**
* Supposed to be true for a single LLVM module within final binary.
*/
val CompilerOutputKind.isFinalBinary: Boolean get() = when (this) {
CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC,
CompilerOutputKind.STATIC, CompilerOutputKind.FRAMEWORK -> true
CompilerOutputKind.DYNAMIC_CACHE, CompilerOutputKind.STATIC_CACHE,
CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false
}
val CompilerOutputKind.involvesBitcodeGeneration: Boolean
get() = this != CompilerOutputKind.LIBRARY
internal val Context.producedLlvmModuleContainsStdlib: Boolean
get() = this.llvmModuleSpecification.containsModule(this.stdlibModule)
val CompilerOutputKind.involvesLinkStage: Boolean
get() = when (this) {
CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC,
CompilerOutputKind.DYNAMIC_CACHE, CompilerOutputKind.STATIC_CACHE,
CompilerOutputKind.STATIC, CompilerOutputKind.FRAMEWORK -> true
CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false
}
val CompilerOutputKind.isCache: Boolean
get() = (this == CompilerOutputKind.STATIC_CACHE || this == CompilerOutputKind.DYNAMIC_CACHE)
internal fun produceCStubs(context: Context) {
val llvmModule = context.llvmModule!!
context.cStubsManager.compile(context.config.clang, context.messageCollector, context.inVerbosePhase)?.let {
parseAndLinkBitcodeFile(llvmModule, it.absolutePath)
}
}
private fun linkAllDependencies(context: Context, generatedBitcodeFiles: List<String>) {
val config = context.config
val runtimeNativeLibraries = config.runtimeNativeLibraries
.takeIf { context.producedLlvmModuleContainsStdlib }.orEmpty()
val launcherNativeLibraries = config.launcherNativeLibraries
.takeIf { config.produce == CompilerOutputKind.PROGRAM }.orEmpty()
linkObjC(context)
val nativeLibraries = config.nativeLibraries + runtimeNativeLibraries + launcherNativeLibraries
val bitcodeLibraries = context.llvm.bitcodeToLink.map { it.bitcodePaths }.flatten().filter { it.isBitcode }
val additionalBitcodeFilesToLink = context.llvm.additionalProducedBitcodeFiles
val exceptionsSupportNativeLibrary = config.exceptionsSupportNativeLibrary
val bitcodeFiles = (nativeLibraries + generatedBitcodeFiles + additionalBitcodeFilesToLink + bitcodeLibraries).toMutableSet()
if (config.produce == CompilerOutputKind.DYNAMIC_CACHE)
bitcodeFiles += exceptionsSupportNativeLibrary
val llvmModule = context.llvmModule!!
bitcodeFiles.forEach {
parseAndLinkBitcodeFile(llvmModule, it)
}
}
private fun insertAliasToEntryPoint(context: Context) {
val nomain = context.config.configuration.get(KonanConfigKeys.NOMAIN) ?: false
if (context.config.produce != CompilerOutputKind.PROGRAM || nomain)
return
val module = context.llvmModule
val entryPoint = LLVMGetNamedFunction(module, "Konan_main")
?: error("Module doesn't contain `Konan_main`")
LLVMAddAlias(module, LLVMTypeOf(entryPoint)!!, entryPoint, "main")
}
internal fun linkBitcodeDependencies(context: Context) {
val config = context.config.configuration
val tempFiles = context.config.tempFiles
val produce = config.get(KonanConfigKeys.PRODUCE)
val generatedBitcodeFiles =
if (produce == CompilerOutputKind.DYNAMIC || produce == CompilerOutputKind.STATIC) {
produceCAdapterBitcode(
context.config.clang,
tempFiles.cAdapterCppName,
tempFiles.cAdapterBitcodeName)
listOf(tempFiles.cAdapterBitcodeName)
} else emptyList()
if (produce == CompilerOutputKind.FRAMEWORK && context.config.produceStaticFramework) {
embedAppleLinkerOptionsToBitcode(context.llvm, context.config)
}
linkAllDependencies(context, generatedBitcodeFiles)
}
internal fun produceOutput(context: Context) {
val config = context.config.configuration
val tempFiles = context.config.tempFiles
val produce = config.get(KonanConfigKeys.PRODUCE)
when (produce) {
CompilerOutputKind.STATIC,
CompilerOutputKind.DYNAMIC,
CompilerOutputKind.FRAMEWORK,
CompilerOutputKind.DYNAMIC_CACHE,
CompilerOutputKind.STATIC_CACHE,
CompilerOutputKind.PROGRAM -> {
val output = tempFiles.nativeBinaryFileName
context.bitcodeFileName = output
// Insert `_main` after pipeline so we won't worry about optimizations
// corrupting entry point.
insertAliasToEntryPoint(context)
LLVMWriteBitcodeToFile(context.llvmModule!!, output)
}
CompilerOutputKind.LIBRARY -> {
val nopack = config.getBoolean(KonanConfigKeys.NOPACK)
val output = context.config.outputFiles.klibOutputFileName(!nopack)
val libraryName = context.config.moduleId
val shortLibraryName = context.config.shortModuleName
val neededLibraries = context.librariesWithDependencies
val abiVersion = KotlinAbiVersion.CURRENT
val compilerVersion = CompilerVersion.CURRENT.toString()
val libraryVersion = config.get(KonanConfigKeys.LIBRARY_VERSION)
val metadataVersion = KlibMetadataVersion.INSTANCE.toString()
val irVersion = KlibIrVersion.INSTANCE.toString()
val versions = KotlinLibraryVersioning(
abiVersion = abiVersion,
libraryVersion = libraryVersion,
compilerVersion = compilerVersion,
metadataVersion = metadataVersion,
irVersion = irVersion
)
val target = context.config.target
val manifestProperties = context.config.manifestProperties
if (!nopack) {
val suffix = context.config.outputFiles.produce.suffix(target)
if (!output.endsWith(suffix)) {
error("please specify correct output: packed: ${!nopack}, $output$suffix")
}
}
val library = buildLibrary(
context.config.nativeLibraries,
context.config.includeBinaries,
neededLibraries,
context.serializedMetadata!!,
context.serializedIr,
versions,
target,
output,
libraryName,
nopack,
shortLibraryName,
manifestProperties,
context.dataFlowGraph)
context.bitcodeFileName = library.mainBitcodeFileName
}
CompilerOutputKind.BITCODE -> {
val output = context.config.outputFile
context.bitcodeFileName = output
LLVMWriteBitcodeToFile(context.llvmModule!!, output)
}
}
}
private fun parseAndLinkBitcodeFile(llvmModule: LLVMModuleRef, path: String) {
val parsedModule = parseBitcodeFile(path)
val failed = LLVMLinkModules2(llvmModule, parsedModule)
if (failed != 0) {
throw Error("failed to link $path") // TODO: retrieve error message from LLVM.
}
}
private fun embedAppleLinkerOptionsToBitcode(llvm: Llvm, config: KonanConfig) {
fun findEmbeddableOptions(options: List<String>): List<List<String>> {
val result = mutableListOf<List<String>>()
val iterator = options.iterator()
loop@while (iterator.hasNext()) {
val option = iterator.next()
result += when {
option.startsWith("-l") -> listOf(option)
option == "-framework" && iterator.hasNext() -> listOf(option, iterator.next())
else -> break@loop // Ignore the rest.
}
}
return result
}
val optionsToEmbed = findEmbeddableOptions(config.platform.configurables.linkerKonanFlags) +
llvm.allNativeDependencies.flatMap { findEmbeddableOptions(it.linkerOpts) }
embedLlvmLinkOptions(llvm.llvmModule, optionsToEmbed)
}
@@ -0,0 +1,474 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import llvm.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.library.SerializedMetadata
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD
import org.jetbrains.kotlin.backend.konan.optimizations.Devirtualization
import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG
import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import java.lang.System.out
import kotlin.LazyThreadSafetyMode.PUBLICATION
import kotlin.reflect.KProperty
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyToWithoutSuperTypes
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
import org.jetbrains.kotlin.konan.util.disposeNativeMemoryAllocator
import org.jetbrains.kotlin.library.SerializedIrModule
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
/**
* Offset for synthetic elements created by lowerings and not attributable to other places in the source code.
*/
internal class SpecialDeclarationsFactory(val context: Context) {
private val enumSpecialDeclarationsFactory by lazy { EnumSpecialDeclarationsFactory(context) }
private val outerThisFields = mutableMapOf<IrClass, IrField>()
private val internalLoweredEnums = mutableMapOf<IrClass, InternalLoweredEnum>()
private val externalLoweredEnums = mutableMapOf<IrClass, ExternalLoweredEnum>()
private data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections)
private val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
val loweredInlineFunctions = mutableSetOf<IrFunction>()
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
fun getOuterThisField(innerClass: IrClass): IrField =
if (!innerClass.isInner) throw AssertionError("Class is not inner: ${innerClass.descriptor}")
else outerThisFields.getOrPut(innerClass) {
val outerClass = innerClass.parent as? IrClass
?: throw AssertionError("No containing class for inner class ${innerClass.descriptor}")
val receiver = ReceiverParameterDescriptorImpl(
innerClass.descriptor,
ImplicitClassReceiver(innerClass.descriptor, null),
Annotations.EMPTY
)
val descriptor = PropertyDescriptorImpl.create(
innerClass.descriptor, Annotations.EMPTY, Modality.FINAL,
DescriptorVisibilities.PRIVATE, false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE, false, false, false, false, false, false
).apply {
this.setType(outerClass.descriptor.defaultType, emptyList(), receiver, null)
initialize(null, null)
}
IrFieldImpl(
startOffset = innerClass.startOffset,
endOffset = innerClass.endOffset,
origin = DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS,
symbol = IrFieldSymbolImpl(descriptor),
name = descriptor.name,
type = outerClass.defaultType,
visibility = descriptor.visibility,
isFinal = !descriptor.isVar,
isExternal = descriptor.isEffectivelyExternal(),
isStatic = descriptor.dispatchReceiverParameter == null
).apply {
parent = innerClass
}
}
fun getLoweredEnum(enumClass: IrClass): LoweredEnumAccess {
assert(enumClass.kind == ClassKind.ENUM_CLASS) { "Expected enum class but was: ${enumClass.descriptor}" }
return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) {
externalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createExternalLoweredEnum(enumClass)
}
} else {
internalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass)
}
}
}
fun getInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum {
assert(enumClass.kind == ClassKind.ENUM_CLASS) { "Expected enum class but was: ${enumClass.descriptor}" }
assert(context.llvmModuleSpecification.containsDeclaration(enumClass)) { "Expected enum class from current module." }
return internalLoweredEnums.getOrPut(enumClass) {
enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass)
}
}
fun getEnumEntryOrdinal(enumEntry: IrEnumEntry) =
enumEntry.parentAsClass.declarations.filterIsInstance<IrEnumEntry>().indexOf(enumEntry)
fun getBridge(overriddenFunction: OverriddenFunctionInfo): IrSimpleFunction {
val irFunction = overriddenFunction.function
assert(overriddenFunction.needBridge) {
"Function ${irFunction.descriptor} is not needed in a bridge to call overridden function ${overriddenFunction.overriddenFunction.descriptor}"
}
val key = BridgeKey(irFunction, overriddenFunction.bridgeDirections)
return bridges.getOrPut(key) { createBridge(key) }
}
private fun createBridge(key: BridgeKey): IrSimpleFunction {
val (function, bridgeDirections) = key
val startOffset = function.startOffset
val endOffset = function.endOffset
fun BridgeDirection.type() =
if (this.kind == BridgeDirectionKind.NONE)
null
else this.irClass?.defaultType ?: context.irBuiltIns.anyNType
return IrFunctionImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_BRIDGE_METHOD(function),
IrSimpleFunctionSymbolImpl(),
"<bridge-$bridgeDirections>${function.computeFunctionName()}".synthesizedName,
function.visibility,
function.modality,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = function.isSuspend,
returnType = bridgeDirections.returnDirection.type() ?: function.returnType,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).apply {
val bridge = this
parent = function.parent
dispatchReceiverParameter = function.dispatchReceiverParameter?.let {
it.copyTo(bridge, type = bridgeDirections.dispatchReceiverDirection.type() ?: it.type)
}
extensionReceiverParameter = function.extensionReceiverParameter?.let {
it.copyTo(bridge, type = bridgeDirections.extensionReceiverDirection.type() ?: it.type)
}
valueParameters += function.valueParameters.map {
it.copyTo(bridge, type = bridgeDirections.parameterDirectionAt(it.index).type() ?: it.type)
}
typeParameters += function.typeParameters.map { parameter ->
parameter.copyToWithoutSuperTypes(bridge).also { it.superTypes += parameter.superTypes }
}
}
}
}
internal class Context(config: KonanConfig) : KonanBackendContext(config) {
lateinit var frontendServices: FrontendServices
lateinit var environment: KotlinCoreEnvironment
lateinit var bindingContext: BindingContext
lateinit var moduleDescriptor: ModuleDescriptor
lateinit var objCExport: ObjCExport
lateinit var cAdapterGenerator: CAdapterGenerator
lateinit var expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>
override val builtIns: KonanBuiltIns by lazy(PUBLICATION) {
moduleDescriptor.builtIns as KonanBuiltIns
}
override val configuration get() = config.configuration
override val internalPackageFqn: FqName = RuntimeNames.kotlinNativeInternalPackageName
val phaseConfig = config.phaseConfig
private val packageScope by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.internalPackageName).memberScope }
val nativePtr by lazy { packageScope.getContributedClassifier(NATIVE_PTR_NAME) as ClassDescriptor }
val nonNullNativePtr by lazy { packageScope.getContributedClassifier(NON_NULL_NATIVE_PTR_NAME) as ClassDescriptor }
val getNativeNullPtr by lazy { packageScope.getContributedFunctions("getNativeNullPtr").single() }
val immutableBlobOf by lazy {
builtIns.builtInsModule.getPackage(KonanFqNames.packageName).memberScope.getContributedFunctions("immutableBlobOf").single()
}
val specialDeclarationsFactory = SpecialDeclarationsFactory(this)
open class LazyMember<T>(val initializer: Context.() -> T) {
operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this)
}
class LazyVarMember<T>(initializer: Context.() -> T) : LazyMember<T>(initializer) {
operator fun setValue(thisRef: Context, property: KProperty<*>, newValue: T) = thisRef.setValue(this, newValue)
}
companion object {
fun <T> lazyMember(initializer: Context.() -> T) = LazyMember<T>(initializer)
fun <K, V> lazyMapMember(initializer: Context.(K) -> V): LazyMember<(K) -> V> = lazyMember {
val storage = mutableMapOf<K, V>()
val result: (K) -> V = {
storage.getOrPut(it, { initializer(it) })
}
result
}
fun <T> nullValue() = LazyVarMember<T?>({ null })
}
private val lazyValues = mutableMapOf<LazyMember<*>, Any?>()
fun <T> getValue(member: LazyMember<T>): T =
@Suppress("UNCHECKED_CAST") (lazyValues.getOrPut(member, { member.initializer(this) }) as T)
fun <T> setValue(member: LazyVarMember<T>, newValue: T) {
lazyValues[member] = newValue
}
val reflectionTypes: KonanReflectionTypes by lazy(PUBLICATION) {
KonanReflectionTypes(moduleDescriptor, KonanFqNames.internalPackageName)
}
// TODO: Remove after adding special <userData> property to IrDeclaration.
private val layoutBuilders = mutableMapOf<IrClass, ClassLayoutBuilder>()
fun getLayoutBuilder(irClass: IrClass): ClassLayoutBuilder {
if (irClass is IrLazyClass)
return layoutBuilders.getOrPut(irClass) {
ClassLayoutBuilder(irClass, this, isLowered = shouldLower(this, irClass))
}
val metadata = irClass.metadata as? CodegenClassMetadata
?: CodegenClassMetadata(irClass).also { irClass.metadata = it }
metadata.layoutBuilder?.let { return it }
val layoutBuilder = ClassLayoutBuilder(irClass, this, isLowered = shouldLower(this, irClass))
metadata.layoutBuilder = layoutBuilder
return layoutBuilder
}
lateinit var globalHierarchyAnalysisResult: GlobalHierarchyAnalysisResult
// We serialize untouched descriptor tree and IR.
// But we have to wait until the code generation phase,
// to dump this information into generated file.
var serializedMetadata: SerializedMetadata? = null
var serializedIr: SerializedIrModule? = null
var dataFlowGraph: ByteArray? = null
val librariesWithDependencies by lazy {
config.librariesWithDependencies(moduleDescriptor)
}
var functionReferenceCount = 0
var coroutineCount = 0
fun needGlobalInit(field: IrField): Boolean {
if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return false
// TODO: add some smartness here. Maybe if package of the field is in never accessed
// assume its global init can be actually omitted.
return true
}
lateinit var irModules: Map<String, IrModuleFragment>
// TODO: make lateinit?
var irModule: IrModuleFragment? = null
set(module) {
if (field != null) {
throw Error("Another IrModule in the context.")
}
field = module!!
ir = KonanIr(this, module)
}
override lateinit var ir: KonanIr
override val irBuiltIns
get() = ir.irModule.irBuiltins
val interopBuiltIns by lazy {
InteropBuiltIns(this.builtIns)
}
var llvmModule: LLVMModuleRef? = null
set(module) {
if (field != null) {
throw Error("Another LLVMModule in the context.")
}
field = module!!
llvm = Llvm(this, module)
debugInfo = DebugInfo(this)
}
lateinit var llvm: Llvm
val llvmImports: LlvmImports = Llvm.ImportsImpl(this)
lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String
lateinit var library: KonanLibraryLayout
private var llvmDisposed = false
fun disposeLlvm() {
if (llvmDisposed) return
if (::debugInfo.isInitialized)
LLVMDisposeDIBuilder(debugInfo.builder)
if (llvmModule != null)
LLVMDisposeModule(llvmModule)
if (::llvm.isInitialized)
LLVMDisposeModule(llvm.runtime.llvmModule)
tryDisposeLLVMContext()
llvmDisposed = true
}
private var nativeMemFreed = false
fun freeNativeMem() {
if (nativeMemFreed) return
disposeNativeMemoryAllocator()
nativeMemFreed = true
}
val cStubsManager = CStubsManager(config.target)
val coverage = CoverageManager(this)
protected fun separator(title: String) {
println("\n\n--- ${title} ----------------------\n")
}
fun verifyDescriptors() {
// TODO: Nothing here for now.
}
fun printDescriptors() {
if (!::moduleDescriptor.isInitialized)
return
separator("Descriptors:")
moduleDescriptor.deepPrint()
}
fun printIr() {
if (irModule == null) return
separator("IR:")
irModule!!.accept(DumpIrTreeVisitor(out), "")
}
fun verifyBitCode() {
if (llvmModule == null) return
verifyModule(llvmModule!!)
}
fun printBitCode() {
if (llvmModule == null) return
separator("BitCode:")
LLVMDumpModule(llvmModule!!)
}
fun verify() {
verifyDescriptors()
verifyBitCode()
}
fun print() {
printDescriptors()
printIr()
printBitCode()
}
fun shouldVerifyBitCode() = config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)
fun shouldPrintBitCode() = config.configuration.getBoolean(KonanConfigKeys.PRINT_BITCODE)
fun shouldPrintLocations() = config.configuration.getBoolean(KonanConfigKeys.PRINT_LOCATIONS)
fun shouldProfilePhases() = config.phaseConfig.needProfiling
fun shouldContainDebugInfo() = config.debug
fun shouldContainLocationDebugInfo() = shouldContainDebugInfo() || config.lightDebug
fun shouldContainAnyDebugInfo() = shouldContainDebugInfo() || shouldContainLocationDebugInfo()
fun shouldOptimize() = config.configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)
fun ghaEnabled() = ::globalHierarchyAnalysisResult.isInitialized
val memoryModel = config.memoryModel
override var inVerbosePhase = false
override fun log(message: () -> String) {
if (inVerbosePhase) {
println(message())
}
}
lateinit var debugInfo: DebugInfo
var moduleDFG: ModuleDFG? = null
var externalModulesDFG: ExternalModulesDFG? = null
lateinit var lifetimes: MutableMap<IrElement, Lifetime>
lateinit var codegenVisitor: CodeGeneratorVisitor
var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null
var referencedFunctions: Set<IrFunction>? = null
val isNativeLibrary: Boolean by lazy {
val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC
}
internal val stdlibModule
get() = this.builtIns.any.module
lateinit var compilerOutput: List<ObjectFile>
val llvmModuleSpecification: LlvmModuleSpecification by lazy {
when {
config.produce.isCache ->
CacheLlvmModuleSpecification(config.cachedLibraries, config.librariesToCache)
else -> DefaultLlvmModuleSpecification(config.cachedLibraries)
}
}
val declaredLocalArrays: MutableMap<String, LLVMTypeRef> = HashMap()
/**
* Manages internal ABI references and declarations.
*/
val internalAbi = InternalAbi(this)
}
private fun MemberScope.getContributedClassifier(name: String) =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
private fun MemberScope.getContributedFunctions(name: String) =
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
internal class ContextLogger(val context: Context) {
operator fun String.unaryPlus() = context.log { this }
}
internal fun Context.logMultiple(messageBuilder: ContextLogger.() -> Unit) {
if (!inVerbosePhase) return
with(ContextLogger(this)) { messageBuilder() }
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
// Must match `DestroyRuntimeMode` in Runtime.h
enum class DestroyRuntimeMode(val value: Int) {
LEGACY(0),
ON_SHUTDOWN(1),
}
@@ -0,0 +1,92 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.irCatch
import org.jetbrains.kotlin.name.Name
internal fun makeEntryPoint(context: Context): IrFunction {
val actualMain = context.ir.symbols.entryPoint!!.owner
val entryPoint = IrFunctionImpl(
actualMain.startOffset,
actualMain.startOffset,
IrDeclarationOrigin.DEFINED,
IrSimpleFunctionSymbolImpl(),
Name.identifier("Konan_start"),
DescriptorVisibilities.PRIVATE,
Modality.FINAL,
context.irBuiltIns.intType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).also { function ->
function.valueParameters = listOf(
IrValueParameterImpl(
actualMain.startOffset, actualMain.startOffset,
IrDeclarationOrigin.DEFINED,
IrValueParameterSymbolImpl(),
Name.identifier("args"),
index = 0,
varargElementType = null,
isCrossinline = false,
type = context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.stringType),
isNoinline = false,
isHidden = false,
isAssignable = false
).apply {
parent = function
})
}
entryPoint.annotations += buildSimpleAnnotation(context.irBuiltIns,
actualMain.startOffset, actualMain.startOffset,
context.ir.symbols.exportForCppRuntime.owner, "Konan_start")
val builder = context.createIrBuilder(entryPoint.symbol)
entryPoint.body = builder.irBlockBody(entryPoint) {
+IrTryImpl(
startOffset = actualMain.startOffset,
endOffset = actualMain.startOffset,
type = context.irBuiltIns.nothingType
).apply {
tryResult = irBlock {
+irCall(actualMain).apply {
if (actualMain.valueParameters.size != 0)
putValueArgument(0, irGet(entryPoint.valueParameters[0]))
}
+irReturn(irInt(0))
}
catches += irCatch(context.irBuiltIns.throwableType).apply {
result = irBlock {
+irCall(context.ir.symbols.onUnhandledException).apply {
putValueArgument(0, irGet(catchParameter))
}
+irReturn(irInt(1))
}
}
}
}
return entryPoint
}
@@ -0,0 +1,219 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.ir.addFakeOverrides
import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
internal object DECLARATION_ORIGIN_ENUM : IrDeclarationOriginImpl("ENUM")
/**
* Common interface for both [InternalLoweredEnum] and [ExternalLoweredEnum]
* that allows to work with lowered enum regardless of its location.
*/
internal interface LoweredEnumAccess {
val valuesGetter: IrSimpleFunction
val itemGetterSymbol: IrSimpleFunctionSymbol
val entriesMap: Map<Name, Int>
fun getValuesField(startOffset: Int, endOffset: Int): IrExpression
}
/**
* Represents lowered enum from current module.
*/
internal data class InternalLoweredEnum(
val implObject: IrClass,
val valuesField: IrField,
val valuesGetterWrapper: IrSimpleFunction,
override val valuesGetter: IrSimpleFunction,
override val itemGetterSymbol: IrSimpleFunctionSymbol,
override val entriesMap: Map<Name, Int>
) : LoweredEnumAccess {
private fun internalObjectGetter(startOffset: Int, endOffset: Int) =
IrGetObjectValueImpl(startOffset, endOffset,
implObject.defaultType,
implObject.symbol
)
override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression = IrGetFieldImpl(
startOffset,
endOffset,
valuesField.symbol,
valuesField.type,
internalObjectGetter(startOffset, endOffset)
)
}
/**
* Represents lowered enum that's located in external module.
*/
internal data class ExternalLoweredEnum(
override val valuesGetter: IrSimpleFunction,
override val itemGetterSymbol: IrSimpleFunctionSymbol,
override val entriesMap: Map<Name, Int>
) : LoweredEnumAccess {
override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression =
IrCallImpl(startOffset, endOffset, valuesGetter.returnType, valuesGetter.symbol, valuesGetter.typeParameters.size, valuesGetter.valueParameters.size)
}
internal class EnumSpecialDeclarationsFactory(val context: Context) {
private val symbols = context.ir.symbols
private fun enumEntriesMap(enumClass: IrClass): Map<Name, Int> =
enumClass.declarations
.filterIsInstance<IrEnumEntry>()
.sortedBy { it.name }
.withIndex()
.associate { it.value.name to it.index }
.toMap()
private fun findItemGetterSymbol(): IrSimpleFunctionSymbol =
symbols.array.functions.single { it.descriptor.name == Name.identifier("get") }
private fun valuesArrayType(enumClass: IrClass): IrType =
symbols.array.typeWith(enumClass.defaultType)
// We can't move property getter to the top-level scope.
// So add a wrapper instead.
private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction =
context.irFactory.buildFun {
name = InternalAbi.getEnumValuesAccessorName(enumClass)
returnType = valuesArrayType(enumClass)
origin = InternalAbi.INTERNAL_ABI_ORIGIN
this.isExternal = isExternal
}.also {
if (isExternal) {
context.internalAbi.reference(it, enumClass.module)
} else {
context.internalAbi.declare(it, enumClass.module)
}
}
fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum {
val enumEntriesMap = enumEntriesMap(enumClass)
val itemGetterSymbol = findItemGetterSymbol()
val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = true)
return ExternalLoweredEnum(valuesGetterWrapper, itemGetterSymbol, enumEntriesMap)
}
fun createInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum {
val startOffset = enumClass.startOffset
val endOffset = enumClass.endOffset
val implObject =
IrClassImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrClassSymbolImpl(),
"OBJECT".synthesizedName,
ClassKind.OBJECT,
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
isCompanion = false,
isInner = false,
isData = false,
isExternal = false,
isInline = false,
isExpect = false,
isFun = false
).apply {
parent = enumClass
createParameterDeclarations()
}
val valuesType = valuesArrayType(enumClass)
val valuesField =
IrFieldImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrFieldSymbolImpl(),
"VALUES".synthesizedName,
valuesType,
DescriptorVisibilities.PRIVATE,
isFinal = true,
isExternal = false,
isStatic = false,
).apply {
parent = implObject
}
val valuesGetter =
IrFunctionImpl(
startOffset, endOffset,
DECLARATION_ORIGIN_ENUM,
IrSimpleFunctionSymbolImpl(),
"get-VALUES".synthesizedName,
DescriptorVisibilities.PUBLIC,
Modality.FINAL,
valuesType,
isInline = false,
isExternal = false,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
).apply {
parent = implObject
}
val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first()
implObject.addSimpleDelegatingConstructor(
constructorOfAny,
context.irBuiltIns,
true // TODO: why primary?
)
implObject.superTypes += context.irBuiltIns.anyType
implObject.addFakeOverrides(context.irBuiltIns)
val itemGetterSymbol = findItemGetterSymbol()
val enumEntriesMap = enumEntriesMap(enumClass)
val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = false)
context.createIrBuilder(valuesGetterWrapper.symbol).run {
valuesGetterWrapper.body = irBlockBody {
+irReturn(irCall(valuesGetter))
}
}
return InternalLoweredEnum(
implObject,
valuesField,
valuesGetterWrapper,
valuesGetter,
itemGetterSymbol,
enumEntriesMap)
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.konan.KonanException
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
/**
* Represents a compilation error caused by mistakes in an input file, e.g. undefined reference.
*/
class KonanCompilationException(
message: String = "",
cause: Throwable? = null
) : KotlinExceptionWithAttachments(message, cause)
/**
* Internal compiler error: could not deserialize IR for inline function body.
*/
class KonanIrDeserializationException(message: String = "", cause: Throwable? = null) : KonanException(message, cause)
@@ -0,0 +1,187 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.konan.*
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.SearchPathResolver
import org.jetbrains.kotlin.library.isInterop
import org.jetbrains.kotlin.library.toUnresolvedLibraries
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal fun Context.getExportedDependencies(): List<ModuleDescriptor> = getDescriptorsFromLibraries((config.resolve.exportedLibraries + config.resolve.includedLibraries).toSet())
internal fun Context.getIncludedLibraryDescriptors(): List<ModuleDescriptor> = getDescriptorsFromLibraries(config.resolve.includedLibraries.toSet())
private fun Context.getDescriptorsFromLibraries(libraries: Set<KonanLibrary>) =
moduleDescriptor.allDependencyModules.filter {
when (val origin = it.klibModuleOrigin) {
CurrentKlibModuleOrigin, SyntheticModulesOrigin -> false
is DeserializedKlibModuleOrigin -> origin.library in libraries
}
}
internal fun getExportedLibraries(
configuration: CompilerConfiguration,
resolvedLibraries: KotlinLibraryResolveResult,
resolver: SearchPathResolver<KonanLibrary>,
report: Boolean
): List<KonanLibrary> = getFeaturedLibraries(
configuration.getList(KonanConfigKeys.EXPORTED_LIBRARIES),
resolvedLibraries,
resolver,
if (report) FeaturedLibrariesReporter.forExportedLibraries(configuration) else FeaturedLibrariesReporter.Silent,
allowDefaultLibs = false
)
internal fun getIncludedLibraries(
includedLibraryFiles: List<File>,
configuration: CompilerConfiguration,
resolvedLibraries: KotlinLibraryResolveResult
): List<KonanLibrary> = getFeaturedLibraries(
includedLibraryFiles.toSet(),
resolvedLibraries,
FeaturedLibrariesReporter.forIncludedLibraries(configuration),
allowDefaultLibs = false
)
internal fun getCoveredLibraries(
configuration: CompilerConfiguration,
resolvedLibraries: KotlinLibraryResolveResult,
resolver: SearchPathResolver<KonanLibrary>
): List<KonanLibrary> = getFeaturedLibraries(
configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER),
resolvedLibraries,
resolver,
FeaturedLibrariesReporter.forCoveredLibraries(configuration),
allowDefaultLibs = true
)
private sealed class FeaturedLibrariesReporter {
abstract fun reportIllegalKind(library: KonanLibrary)
abstract fun reportNotIncludedLibraries(includedLibraries: List<KonanLibrary>, remainingFeaturedLibraries: Set<File>)
protected val KonanLibrary.reportedKind: String
get() = when {
isInterop -> "Interop"
isDefault -> "Default"
else -> "Unknown kind"
}
object Silent: FeaturedLibrariesReporter() {
override fun reportIllegalKind(library: KonanLibrary) {}
override fun reportNotIncludedLibraries(includedLibraries: List<KonanLibrary>, remainingFeaturedLibraries: Set<File>) {}
}
abstract class BaseReporter(val configuration: CompilerConfiguration) : FeaturedLibrariesReporter() {
protected abstract fun illegalKindMessage(kind: String, libraryName: String): String
protected abstract fun notIncludedLibraryMessageTitle(): String
override fun reportIllegalKind(library: KonanLibrary) {
configuration.report(
CompilerMessageSeverity.STRONG_WARNING,
illegalKindMessage(library.reportedKind, library.libraryName)
)
}
override fun reportNotIncludedLibraries(includedLibraries: List<KonanLibrary>, remainingFeaturedLibraries: Set<File>) {
val message = buildString {
appendLine(notIncludedLibraryMessageTitle())
remainingFeaturedLibraries.forEach { appendLine(it) }
appendLine()
appendLine("Included libraries:")
includedLibraries.forEach { appendLine(it.libraryFile) }
}
configuration.report(CompilerMessageSeverity.STRONG_WARNING, message)
}
}
private class IncludedLibrariesReporter(val configuration: CompilerConfiguration) : FeaturedLibrariesReporter() {
override fun reportIllegalKind(library: KonanLibrary) = with(library) {
val message = "$reportedKind library $libraryName cannot be passed with -Xinclude " +
"(library path: ${libraryFile.absolutePath})"
configuration.report(CompilerMessageSeverity.STRONG_WARNING, message)
}
override fun reportNotIncludedLibraries(includedLibraries: List<KonanLibrary>, remainingFeaturedLibraries: Set<File>) {
error("An included library is not found among resolved libraries")
}
}
private class ExportedLibrariesReporter(configuration: CompilerConfiguration) : BaseReporter(configuration) {
override fun illegalKindMessage(kind: String, libraryName: String): String =
"$kind library $libraryName can't be exported with -Xexport-library"
override fun notIncludedLibraryMessageTitle(): String =
"Following libraries are specified to be exported with -Xexport-library, but not included to the build:"
}
private class CoveredLibraryReporter(configuration: CompilerConfiguration): BaseReporter(configuration) {
override fun illegalKindMessage(kind: String, libraryName: String): String =
"Cannot provide the code coverage for the $kind library $libraryName."
override fun notIncludedLibraryMessageTitle(): String =
"The code coverage is enabled for the following libraries, but they are not included to the build:"
}
companion object {
fun forExportedLibraries(configuration: CompilerConfiguration): FeaturedLibrariesReporter =
ExportedLibrariesReporter(configuration)
fun forCoveredLibraries(configuration: CompilerConfiguration): FeaturedLibrariesReporter =
CoveredLibraryReporter(configuration)
fun forIncludedLibraries(configuration: CompilerConfiguration): FeaturedLibrariesReporter =
IncludedLibrariesReporter(configuration)
}
}
private fun getFeaturedLibraries(
featuredLibraries: List<String>,
resolvedLibraries: KotlinLibraryResolveResult,
resolver: SearchPathResolver<KonanLibrary>,
reporter: FeaturedLibrariesReporter,
allowDefaultLibs: Boolean
) = getFeaturedLibraries(
featuredLibraries.toUnresolvedLibraries.map { resolver.resolve(it).libraryFile }.toSet(),
resolvedLibraries,
reporter,
allowDefaultLibs
)
private fun getFeaturedLibraries(
featuredLibraryFiles: Set<File>,
resolvedLibraries: KotlinLibraryResolveResult,
reporter: FeaturedLibrariesReporter,
allowDefaultLibs: Boolean
) : List<KonanLibrary> {
val remainingFeaturedLibraries = featuredLibraryFiles.toMutableSet()
val result = mutableListOf<KonanLibrary>()
//TODO: please add type checks before cast.
val libraries = resolvedLibraries.getFullList(null).cast<List<KonanLibrary>>()
for (library in libraries) {
val libraryFile = library.libraryFile
if (libraryFile in featuredLibraryFiles) {
remainingFeaturedLibraries -= libraryFile
if (library.isInterop || (!allowDefaultLibs && library.isDefault)) {
reporter.reportIllegalKind(library)
} else {
result += library
}
}
}
if (remainingFeaturedLibraries.isNotEmpty()) {
reporter.reportNotIncludedLibraries(libraries, remainingFeaturedLibraries)
}
return result
}
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
internal interface DirectedGraphNode<out K> {
val key: K
val directEdges: List<K>?
val reversedEdges: List<K>?
}
internal interface DirectedGraph<K, out N: DirectedGraphNode<K>> {
val nodes: Collection<N>
fun get(key: K): N
}
internal class DirectedGraphMultiNode<out K>(val nodes: Set<K>)
internal class DirectedGraphCondensation<out K>(val topologicalOrder: List<DirectedGraphMultiNode<K>>)
// The Kosoraju-Sharir algorithm.
internal class DirectedGraphCondensationBuilder<K, out N: DirectedGraphNode<K>>(private val graph: DirectedGraph<K, N>) {
private val visited = mutableSetOf<K>()
private val order = mutableListOf<N>()
private val nodeToMultiNodeMap = mutableMapOf<N, DirectedGraphMultiNode<K>>()
private val multiNodesOrder = mutableListOf<DirectedGraphMultiNode<K>>()
fun build(): DirectedGraphCondensation<K> {
// First phase.
graph.nodes.forEach {
if (!visited.contains(it.key))
findOrder(it)
}
// Second phase.
visited.clear()
val multiNodes = mutableListOf<DirectedGraphMultiNode<K>>()
order.reversed().forEach {
if (!visited.contains(it.key)) {
val nodes = mutableSetOf<K>()
paint(it, nodes)
multiNodes += DirectedGraphMultiNode(nodes)
}
}
// Topsort of built condensation.
multiNodes.forEach { multiNode ->
multiNode.nodes.forEach { nodeToMultiNodeMap.put(graph.get(it), multiNode) }
}
visited.clear()
multiNodes.forEach {
if (!visited.contains(it.nodes.first()))
findMultiNodesOrder(it)
}
return DirectedGraphCondensation(multiNodesOrder.reversed())
}
private fun findOrder(node: N) {
visited += node.key
node.directEdges?.forEach {
if (!visited.contains(it))
findOrder(graph.get(it))
}
order += node
}
private fun paint(node: N, multiNode: MutableSet<K>) {
visited += node.key
multiNode += node.key
node.reversedEdges?.forEach {
if (!visited.contains(it))
paint(graph.get(it), multiNode)
}
}
private fun findMultiNodesOrder(node: DirectedGraphMultiNode<K>) {
visited.addAll(node.nodes)
node.nodes.forEach {
graph.get(it).directEdges?.forEach {
if (!visited.contains(it))
findMultiNodesOrder(nodeToMultiNodeMap[graph.get(it)]!!)
}
}
multiNodesOrder += node
}
}
@@ -0,0 +1,319 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.ir.containsNull
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.isPublicApi
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.packageFqName
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
/**
* TODO: there is [IrType::getInlinedClass] in [org.jetbrains.kotlin.ir.util] which isn't compatible with
* Native's implementation. Please take a look while commonization.
*/
fun IrType.getInlinedClassNative(): IrClass? = IrTypeInlineClassesSupport.getInlinedClass(this)
/**
* TODO: there is [IrType::isInlined] in [org.jetbrains.kotlin.ir.util] which isn't compatible with
* Native's implementation. Please take a look while commonization.
*/
fun IrType.isInlinedNative(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isInlined(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isNativePrimitiveType() = IrTypeInlineClassesSupport.isTopLevelClass(this) &&
KonanPrimitiveType.byFqNameParts[packageFqName]?.get(name) != null
fun KotlinType.getInlinedClass(): ClassDescriptor? = KotlinTypeInlineClassesSupport.getInlinedClass(this)
fun ClassDescriptor.isInlined(): Boolean = KotlinTypeInlineClassesSupport.isInlined(this)
fun KotlinType.binaryRepresentationIsNullable() = KotlinTypeInlineClassesSupport.representationIsNullable(this)
internal inline fun <R> KotlinType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: ClassDescriptor, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: KotlinType) -> R
): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
// TODO: consider renaming to `isReference`.
fun KotlinType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun KotlinType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun KotlinType.computeBinaryType(): BinaryType<ClassDescriptor> = KotlinTypeInlineClassesSupport.computeBinaryType(this)
fun IrType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun IrType.computeBinaryType(): BinaryType<IrClass> = IrTypeInlineClassesSupport.computeBinaryType(this)
fun IrClass.inlinedClassIsNullable(): Boolean = this.defaultType.makeNullable().getInlinedClassNative() == this // TODO: optimize
fun IrClass.isUsedAsBoxClass(): Boolean = IrTypeInlineClassesSupport.isUsedAsBoxClass(this)
/**
* Most "underlying" user-visible non-reference type.
* It is visible as inlined to compiler for simplicity.
*/
enum class KonanPrimitiveType(val classId: ClassId, val binaryType: BinaryType.Primitive) {
BOOLEAN(PrimitiveType.BOOLEAN, PrimitiveBinaryType.BOOLEAN),
CHAR(PrimitiveType.CHAR, PrimitiveBinaryType.SHORT),
BYTE(PrimitiveType.BYTE, PrimitiveBinaryType.BYTE),
SHORT(PrimitiveType.SHORT, PrimitiveBinaryType.SHORT),
INT(PrimitiveType.INT, PrimitiveBinaryType.INT),
LONG(PrimitiveType.LONG, PrimitiveBinaryType.LONG),
FLOAT(PrimitiveType.FLOAT, PrimitiveBinaryType.FLOAT),
DOUBLE(PrimitiveType.DOUBLE, PrimitiveBinaryType.DOUBLE),
NON_NULL_NATIVE_PTR(ClassId.topLevel(KonanFqNames.nonNullNativePtr.toSafe()), PrimitiveBinaryType.POINTER),
VECTOR128(ClassId.topLevel(KonanFqNames.Vector128), PrimitiveBinaryType.VECTOR128)
;
constructor(primitiveType: PrimitiveType, primitiveBinaryType: PrimitiveBinaryType)
: this(ClassId.topLevel(primitiveType.typeFqName), primitiveBinaryType)
constructor(classId: ClassId, primitiveBinaryType: PrimitiveBinaryType)
: this(classId, BinaryType.Primitive(primitiveBinaryType))
val fqName: FqNameUnsafe get() = this.classId.asSingleFqName().toUnsafe()
companion object {
val byFqNameParts = KonanPrimitiveType.values().groupingBy {
assert(!it.classId.isNestedClass)
it.classId.packageFqName
}.fold({ _, _ -> mutableMapOf<Name, KonanPrimitiveType>() },
{ _, accumulator, element ->
accumulator.also { it[element.classId.shortClassName] = element }
})
}
}
internal abstract class InlineClassesSupport<Class : Any, Type : Any> {
protected abstract fun isNullable(type: Type): Boolean
protected abstract fun makeNullable(type: Type): Type
protected abstract fun erase(type: Type): Class
protected abstract fun computeFullErasure(type: Type): Sequence<Class>
protected abstract fun hasInlineModifier(clazz: Class): Boolean
protected abstract fun getNativePointedSuperclass(clazz: Class): Class?
protected abstract fun getInlinedClassUnderlyingType(clazz: Class): Type
protected abstract fun getPackageFqName(clazz: Class): FqName?
protected abstract fun getName(clazz: Class): Name?
abstract fun isTopLevelClass(clazz: Class): Boolean
@JvmName("classIsInlined")
fun isInlined(clazz: Class): Boolean = getInlinedClass(clazz) != null
fun isInlined(type: Type): Boolean = getInlinedClass(type) != null
fun isUsedAsBoxClass(clazz: Class) = getInlinedClass(clazz) == clazz // To handle NativePointed subclasses.
fun getInlinedClass(type: Type): Class? =
getInlinedClass(erase(type), isNullable(type))
protected fun getKonanPrimitiveType(clazz: Class): KonanPrimitiveType? =
if (isTopLevelClass(clazz))
KonanPrimitiveType.byFqNameParts[getPackageFqName(clazz)]?.get(getName(clazz))
else null
protected fun isImplicitInlineClass(clazz: Class): Boolean =
isTopLevelClass(clazz) && (getKonanPrimitiveType(clazz) != null ||
getName(clazz) == KonanFqNames.nativePtr.shortName() && getPackageFqName(clazz) == KonanFqNames.internalPackageName ||
getName(clazz) == InteropFqNames.cPointer.shortName() && getPackageFqName(clazz) == InteropFqNames.cPointer.parent().toSafe())
private fun getInlinedClass(erased: Class, isNullable: Boolean): Class? {
val inlinedClass = getInlinedClass(erased) ?: return null
return if (!isNullable || representationIsNonNullReferenceOrPointer(inlinedClass)) {
inlinedClass
} else {
null
}
}
tailrec fun representationIsNonNullReferenceOrPointer(clazz: Class): Boolean {
val konanPrimitiveType = getKonanPrimitiveType(clazz)
if (konanPrimitiveType != null) {
return konanPrimitiveType == KonanPrimitiveType.NON_NULL_NATIVE_PTR
}
val inlinedClass = getInlinedClass(clazz) ?: return true
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
return if (isNullable(underlyingType)) {
false
} else {
representationIsNonNullReferenceOrPointer(erase(underlyingType))
}
}
@JvmName("classGetInlinedClass")
private fun getInlinedClass(clazz: Class): Class? =
if (hasInlineModifier(clazz) || isImplicitInlineClass(clazz)) {
clazz
} else {
getNativePointedSuperclass(clazz)
}
inline fun <R> unwrapToPrimitiveOrReference(
type: Type,
eachInlinedClass: (inlinedClass: Class, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: Type) -> R
): R {
var currentType: Type = type
while (true) {
val inlinedClass = getInlinedClass(currentType)
if (inlinedClass == null) {
return ifReference(currentType)
}
val nullable = isNullable(currentType)
getKonanPrimitiveType(inlinedClass)?.let { primitiveType ->
return ifPrimitive(primitiveType, nullable)
}
eachInlinedClass(inlinedClass, nullable)
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
currentType = if (nullable) makeNullable(underlyingType) else underlyingType
}
}
fun representationIsNullable(type: Type): Boolean {
unwrapToPrimitiveOrReference(
type,
eachInlinedClass = { _, nullable -> if (nullable) return true },
ifPrimitive = { _, nullable -> return nullable },
ifReference = { return isNullable(it) }
)
}
// TODO: optimize.
fun computeBinaryType(type: Type): BinaryType<Class> {
val erased = erase(type)
val inlinedClass = getInlinedClass(erased, isNullable(type)) ?: return createReferenceBinaryType(type)
getKonanPrimitiveType(inlinedClass)?.let {
return it.binaryType
}
val underlyingBinaryType = computeBinaryType(getInlinedClassUnderlyingType(inlinedClass))
return if (isNullable(type) && underlyingBinaryType is BinaryType.Reference) {
BinaryType.Reference(underlyingBinaryType.types, true)
} else {
underlyingBinaryType
}
}
private fun createReferenceBinaryType(type: Type): BinaryType.Reference<Class> =
BinaryType.Reference(computeFullErasure(type), true)
}
internal object KotlinTypeInlineClassesSupport : InlineClassesSupport<ClassDescriptor, KotlinType>() {
override fun isNullable(type: KotlinType): Boolean = type.isNullable()
override fun makeNullable(type: KotlinType): KotlinType = type.makeNullable()
override tailrec fun erase(type: KotlinType): ClassDescriptor {
val descriptor = type.constructor.declarationDescriptor
return if (descriptor is ClassDescriptor) {
descriptor
} else {
erase(type.constructor.supertypes.first())
}
}
override fun computeFullErasure(type: KotlinType): Sequence<ClassDescriptor> {
val classifier = type.constructor.declarationDescriptor
return if (classifier is ClassDescriptor) sequenceOf(classifier)
else type.constructor.supertypes.asSequence().flatMap { computeFullErasure(it) }
}
override fun hasInlineModifier(clazz: ClassDescriptor): Boolean = clazz.isInlineClass()
override fun getNativePointedSuperclass(clazz: ClassDescriptor): ClassDescriptor? = clazz.getAllSuperClassifiers()
.firstOrNull { it.fqNameUnsafe == InteropFqNames.nativePointed } as ClassDescriptor?
override fun getInlinedClassUnderlyingType(clazz: ClassDescriptor): KotlinType =
clazz.unsubstitutedPrimaryConstructor!!.valueParameters.single().type
override fun getPackageFqName(clazz: ClassDescriptor) =
clazz.findPackage().fqName
override fun getName(clazz: ClassDescriptor) =
clazz.name
override fun isTopLevelClass(clazz: ClassDescriptor): Boolean = clazz.containingDeclaration is PackageFragmentDescriptor
}
private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.containsNull()
override fun makeNullable(type: IrType): IrType = type.makeNullable()
override tailrec fun erase(type: IrType): IrClass {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}
override fun computeFullErasure(type: IrType): Sequence<IrClass> {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> sequenceOf(classifier.owner)
is IrTypeParameterSymbol -> classifier.owner.superTypes.asSequence().flatMap { computeFullErasure(it) }
else -> error(classifier)
}
}
override fun hasInlineModifier(clazz: IrClass): Boolean = clazz.isInline
override fun getNativePointedSuperclass(clazz: IrClass): IrClass? {
var superClass: IrClass? = clazz
while (superClass != null && (!superClass.symbol.isPublicApi || InteropIdSignatures.nativePointed != superClass.symbol.signature))
superClass = superClass.getSuperClassNotAny()
return superClass
}
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
?: clazz.declarations.filterIsInstance<IrProperty>().single { it.backingField != null }.backingField!!.type
override fun getPackageFqName(clazz: IrClass) =
clazz.packageFqName
override fun getName(clazz: IrClass): Name? =
clazz.name
override fun isTopLevelClass(clazz: IrClass): Boolean = clazz.isTopLevel
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.util.addFile
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
/**
* Sometimes we need to reference symbols that are not declared in metadata.
* For example, symbol might be declared during lowering.
* In case of compiler caches, this means that it is not accessible as Lazy IR
* and we have to explicitly add an external declaration.
*/
internal class InternalAbi(private val context: Context) {
/**
* Files that stores all internal ABI declarations.
* We use per-module files so that global initializer will be stored
* in the appropriate modules.
*
* We have to store such declarations in top-level to avoid mangling that
* makes referencing harder.
* A bit better solution is to add files with proper packages, but it is impossible
* during FileLowering (hello, ConcurrentModificationException).
*/
private lateinit var internalAbiFiles: Map<ModuleDescriptor, IrFile>
/**
* Representation of ABI files from external modules.
*/
private val externalAbiFiles = mutableMapOf<ModuleDescriptor, IrFile>()
fun init(modules: List<IrModuleFragment>) {
internalAbiFiles = modules.associate { it.descriptor to createAbiFile(it) }
}
private fun createAbiFile(module: IrModuleFragment): IrFile =
module.addFile(NaiveSourceBasedFileEntryImpl("internal"), FqName("kotlin.native.caches.abi"))
/**
* Adds external [function] from [module] to a list of external references.
*/
fun reference(function: IrFunction, module: ModuleDescriptor) {
assert(function.isExternal) { "Function that represents external ABI should be marked as external" }
context.llvmImports.add(module.llvmSymbolOrigin)
externalAbiFiles.getOrPut(module) {
createAbiFile(IrModuleFragmentImpl(module, context.irBuiltIns))
}.addChild(function)
}
/**
* Adds [function] to a list of [module]'s publicly available symbols.
*/
fun declare(function: IrFunction, module: ModuleDescriptor) {
internalAbiFiles.getValue(module).addChild(function)
}
companion object {
/**
* Allows to distinguish external declarations to internal ABI.
*/
val INTERNAL_ABI_ORIGIN = object : IrDeclarationOriginImpl("INTERNAL_ABI") {}
fun getCompanionObjectAccessorName(companion: IrClass): Name =
getMangledNameFor("globalAccessor", companion)
fun getEnumValuesAccessorName(enum: IrClass): Name =
getMangledNameFor("getValues", enum)
/**
* Generate name for declaration that will be a part of internal ABI.
*/
private fun getMangledNameFor(declarationName: String, parent: IrDeclarationParent): Name {
val prefix = parent.fqNameForIrSerialization
return "$prefix.$declarationName".synthesizedName
}
}
}
@@ -0,0 +1,129 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.types.getPublicSignature
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.TypeUtils
object InteropFqNames {
const val cPointerName = "CPointer"
const val nativePointedName = "NativePointed"
val packageName = FqName("kotlinx.cinterop")
val cPointer = packageName.child(Name.identifier(cPointerName)).toUnsafe()
val nativePointed = packageName.child(Name.identifier(nativePointedName)).toUnsafe()
}
object InteropIdSignatures {
val nativePointed = getPublicSignature(InteropFqNames.packageName, InteropFqNames.nativePointedName)
}
internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val packageScope = builtIns.builtInsModule.getPackage(InteropFqNames.packageName).memberScope
val nativePointed = packageScope.getContributedClass(InteropFqNames.nativePointedName)
val cValuesRef = this.packageScope.getContributedClass("CValuesRef")
val cValues = this.packageScope.getContributedClass("CValues")
val cValue = this.packageScope.getContributedClass("CValue")
val cOpaque = this.packageScope.getContributedClass("COpaque")
val cValueWrite = this.packageScope.getContributedFunctions("write")
.single { it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == cValue }
val cValueRead = this.packageScope.getContributedFunctions("readValue")
.single { it.valueParameters.size == 1 }
val cEnum = this.packageScope.getContributedClass("CEnum")
val cEnumVar = this.packageScope.getContributedClass("CEnumVar")
val cStructVar = this.packageScope.getContributedClass("CStructVar")
val cStructVarType = cStructVar.defaultType.memberScope.getContributedClass("Type")
val cPrimitiveVar = this.packageScope.getContributedClass("CPrimitiveVar")
val cPrimitiveVarType = cPrimitiveVar.defaultType.memberScope.getContributedClass("Type")
val nativeMemUtils = this.packageScope.getContributedClass("nativeMemUtils")
val allocType = this.packageScope.getContributedFunctions("alloc")
.single { it.extensionReceiverParameter != null
&& it.valueParameters.singleOrNull()?.name?.toString() == "type" }
val cPointer = this.packageScope.getContributedClass(InteropFqNames.cPointerName)
val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single()
val cPointerGetRawValue = packageScope.getContributedFunctions("getRawValue").single {
val extensionReceiverParameter = it.extensionReceiverParameter
extensionReceiverParameter != null &&
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer
}
val cstr = packageScope.getContributedVariables("cstr").single()
val wcstr = packageScope.getContributedVariables("wcstr").single()
val memScope = packageScope.getContributedClass("MemScope")
val nativePointedRawPtrGetter =
nativePointed.unsubstitutedMemberScope.getContributedVariables("rawPtr").single().getter!!
val nativePointedGetRawPointer = packageScope.getContributedFunctions("getRawPointer").single {
val extensionReceiverParameter = it.extensionReceiverParameter
extensionReceiverParameter != null &&
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == nativePointed
}
val typeOf = packageScope.getContributedFunctions("typeOf").single()
private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor =
this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!
val objCObject = packageScope.getContributedClass("ObjCObject")
val objCObjectBase = packageScope.getContributedClass("ObjCObjectBase")
val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single()
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
val objCObjectRawPtr = packageScope.getContributedFunctions("objcPtr").single()
val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single()
val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single()
val interpretNullablePointed = packageScope.getContributedFunctions("interpretNullablePointed").single()
val interpretCPointer = packageScope.getContributedFunctions("interpretCPointer").single()
val objCObjectSuperInitCheck = packageScope.getContributedFunctions("superInitCheck").single()
val objCObjectInitBy = packageScope.getContributedFunctions("initBy").single()
val objCAction = packageScope.getContributedClass("ObjCAction")
val objCOutlet = packageScope.getContributedClass("ObjCOutlet")
val objCOverrideInit = objCObjectBase.unsubstitutedMemberScope.getContributedClass("OverrideInit")
val objCMethodImp = packageScope.getContributedClass("ObjCMethodImp")
val exportObjCClass = packageScope.getContributedClass("ExportObjCClass")
val CreateNSStringFromKString = packageScope.getContributedFunctions("CreateNSStringFromKString").single()
}
private fun MemberScope.getContributedVariables(name: String) =
this.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
private fun MemberScope.getContributedClass(name: String): ClassDescriptor =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) as ClassDescriptor
private fun MemberScope.getContributedFunctions(name: String) =
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
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.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.name.Name
internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBackendContext {
abstract override val builtIns: KonanBuiltIns
abstract override val ir: KonanIr
override val scriptMode: Boolean = false
override val sharedVariablesManager by lazy {
// Creating lazily because builtIns module seems to be incomplete during `link` test;
// TODO: investigate this.
KonanSharedVariablesManager(this)
}
fun getKonanInternalClass(name: String): ClassDescriptor =
builtIns.kotlinNativeInternal.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
fun getKonanInternalFunctions(name: String): List<FunctionDescriptor> =
builtIns.kotlinNativeInternal.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).toList()
val messageCollector: MessageCollector
get() = config.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) {
val location = element?.getCompilerMessageLocation(irFile ?: error("irFile should be not null for $element"))
this.messageCollector.report(
if (isError) CompilerMessageSeverity.ERROR else CompilerMessageSeverity.WARNING,
message, location
)
}
override val internalPackageFqn = KonanFqNames.internalPackageName
override val mapping: Mapping = DefaultMapping()
override val irFactory: IrFactory = IrFactoryImpl
}
internal fun IrElement.getCompilerMessageLocation(containingFile: IrFile): CompilerMessageLocation? =
createCompilerMessageLocation(containingFile, this.startOffset, this.endOffset)
internal fun IrBuilderWithScope.getCompilerMessageLocation(): CompilerMessageLocation? {
val declaration = this.scope.scopeOwnerSymbol.owner as? IrDeclaration ?: return null
val file = declaration.findPackage() as? IrFile ?: return null
return createCompilerMessageLocation(file, startOffset, endOffset)
}
private fun createCompilerMessageLocation(containingFile: IrFile, startOffset: Int, endOffset: Int): CompilerMessageLocation? {
val sourceRangeInfo = containingFile.fileEntry.getSourceRangeInfo(startOffset, endOffset)
return CompilerMessageLocation.create(
path = sourceRangeInfo.filePath,
line = sourceRangeInfo.startLineNumber + 1,
column = sourceRangeInfo.startColumnNumber + 1,
lineContent = null // TODO: retrieve the line content.
)
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportLazy
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportLazyImpl
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector
import org.jetbrains.kotlin.backend.konan.objcexport.dumpObjCHeader
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
internal fun StorageComponentContainer.initContainer(config: KonanConfig) {
useImpl<FrontendServices>()
if (config.configuration.get(KonanConfigKeys.EMIT_LAZY_OBJC_HEADER_FILE) != null) {
useImpl<ObjCExportLazyImpl>()
useInstance(object : ObjCExportProblemCollector {
override fun reportWarning(text: String) {}
override fun reportWarning(method: FunctionDescriptor, text: String) {}
override fun reportException(throwable: Throwable) = throw throwable
})
useInstance(object : ObjCExportLazy.Configuration {
override val frameworkName: String
get() = config.moduleId
override fun isIncluded(moduleInfo: ModuleInfo): Boolean = true
override fun getCompilerModuleName(moduleInfo: ModuleInfo): String {
TODO()
}
override val objcGenerics: Boolean
get() = config.configuration.getBoolean(KonanConfigKeys.OBJC_GENERICS)
})
}
}
internal fun ComponentProvider.postprocessComponents(context: Context, files: Collection<KtFile>) {
context.frontendServices = this.get<FrontendServices>()
context.config.configuration.get(KonanConfigKeys.EMIT_LAZY_OBJC_HEADER_FILE)?.let {
this.get<ObjCExportLazy>().dumpObjCHeader(files, it)
}
}
class FrontendServices(val deprecationResolver: DeprecationResolver)
@@ -0,0 +1,205 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.MetaVersion
import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.properties.loadProperties
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.util.KonanHomeProvider
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.utils.addToStdlib.cast
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
internal val distribution = Distribution(
configuration.get(KonanConfigKeys.KONAN_HOME) ?: KonanHomeProvider.determineKonanHome(),
false,
configuration.get(KonanConfigKeys.RUNTIME_FILE),
configuration.get(KonanConfigKeys.OVERRIDE_KONAN_PROPERTIES)
)
private val platformManager = PlatformManager(distribution)
internal val targetManager = platformManager.targetManager(configuration.get(KonanConfigKeys.TARGET))
internal val target = targetManager.target
internal val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG)!!
// TODO: debug info generation mode and debug/release variant selection probably requires some refactoring.
val debug: Boolean get() = configuration.getBoolean(KonanConfigKeys.DEBUG)
val lightDebug: Boolean = configuration.get(KonanConfigKeys.LIGHT_DEBUG)
?: target.family.isAppleFamily // Default is true for Apple targets.
val memoryModel: MemoryModel get() = configuration.get(KonanConfigKeys.MEMORY_MODEL)!!
val destroyRuntimeMode: DestroyRuntimeMode get() = configuration.get(KonanConfigKeys.DESTROY_RUNTIME_MODE)!!
val needVerifyIr: Boolean
get() = configuration.get(KonanConfigKeys.VERIFY_IR) == true
val needCompilerVerification: Boolean
get() = configuration.get(KonanConfigKeys.VERIFY_COMPILER) ?:
(configuration.getBoolean(KonanConfigKeys.OPTIMIZATION) ||
CompilerVersion.CURRENT.meta != MetaVersion.RELEASE)
init {
if (!platformManager.isEnabled(target)) {
error("Target ${target.visibleName} is not available on the ${HostManager.hostName} host")
}
}
val platform = platformManager.platform(target).apply {
if (configuration.getBoolean(KonanConfigKeys.CHECK_DEPENDENCIES)) {
downloadDependencies()
}
}
internal val clang = platform.clang
val indirectBranchesAreAllowed = target != KonanTarget.WASM32
val threadsAreAllowed = (target != KonanTarget.WASM32) && (target !is KonanTarget.ZEPHYR)
internal val produce get() = configuration.get(KonanConfigKeys.PRODUCE)!!
internal val metadataKlib get() = configuration.get(KonanConfigKeys.METADATA_KLIB)!!
internal val produceStaticFramework get() = configuration.getBoolean(KonanConfigKeys.STATIC_FRAMEWORK)
internal val purgeUserLibs: Boolean
get() = configuration.getBoolean(KonanConfigKeys.PURGE_USER_LIBS)
internal val resolve = KonanLibrariesResolveSupport(configuration, target, distribution)
internal val resolvedLibraries get() = resolve.resolvedLibraries
internal val cacheSupport = CacheSupport(configuration, resolvedLibraries, target, produce)
internal val cachedLibraries: CachedLibraries
get() = cacheSupport.cachedLibraries
internal val librariesToCache: Set<KotlinLibrary>
get() = cacheSupport.librariesToCache
val outputFiles =
OutputFiles(configuration.get(KonanConfigKeys.OUTPUT) ?: cacheSupport.tryGetImplicitOutput(),
target, produce)
val tempFiles = TempFiles(outputFiles.outputName, configuration.get(KonanConfigKeys.TEMPORARY_FILES_DIR))
val outputFile get() = outputFiles.mainFile
val moduleId: String
get() = configuration.get(KonanConfigKeys.MODULE_NAME) ?: File(outputFiles.outputName).name
val shortModuleName: String?
get() = configuration.get(KonanConfigKeys.SHORT_MODULE_NAME)
val infoArgsOnly = configuration.kotlinSourceRoots.isEmpty()
&& configuration[KonanConfigKeys.INCLUDED_LIBRARIES].isNullOrEmpty()
&& librariesToCache.isEmpty()
fun librariesWithDependencies(moduleDescriptor: ModuleDescriptor?): List<KonanLibrary> {
if (moduleDescriptor == null) error("purgeUnneeded() only works correctly after resolve is over, and we have successfully marked package files as needed or not needed.")
return resolvedLibraries.filterRoots { (!it.isDefault && !this.purgeUserLibs) || it.isNeededForLink }.getFullList(TopologicalLibraryOrder).cast()
}
val shouldCoverSources = configuration.getBoolean(KonanConfigKeys.COVERAGE)
private val shouldCoverLibraries = !configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER).isNullOrEmpty()
internal val runtimeNativeLibraries: List<String> = mutableListOf<String>().apply {
add(if (debug) "debug.bc" else "release.bc")
val effectiveMemoryModel = when (memoryModel) {
MemoryModel.STRICT -> MemoryModel.STRICT
MemoryModel.RELAXED -> MemoryModel.RELAXED
MemoryModel.EXPERIMENTAL -> {
if (!target.supportsThreads()) {
configuration.report(CompilerMessageSeverity.STRONG_WARNING,
"Experimental memory model requires threads, which are not supported on target ${target.name}. Used strict memory model.")
MemoryModel.STRICT
} else if (destroyRuntimeMode == DestroyRuntimeMode.LEGACY) {
configuration.report(CompilerMessageSeverity.STRONG_WARNING,
"Experimental memory model is incompatible with 'legacy' destroy runtime mode. Used strict memory model.")
MemoryModel.STRICT
} else {
MemoryModel.EXPERIMENTAL
}
}
}
val useMimalloc = if (configuration.get(KonanConfigKeys.ALLOCATION_MODE) == "mimalloc") {
if (target.supportsMimallocAllocator()) {
true
} else {
configuration.report(CompilerMessageSeverity.STRONG_WARNING,
"Mimalloc allocator isn't supported on target ${target.name}. Used standard mode.")
false
}
} else {
false
}
when (effectiveMemoryModel) {
MemoryModel.STRICT -> {
add("strict.bc")
add("legacy_memory_manager.bc")
}
MemoryModel.RELAXED -> {
add("relaxed.bc")
add("legacy_memory_manager.bc")
}
MemoryModel.EXPERIMENTAL -> {
add("experimental_memory_manager.bc")
}
}
if (shouldCoverLibraries || shouldCoverSources) add("profileRuntime.bc")
if (useMimalloc) {
add("opt_alloc.bc")
add("mimalloc.bc")
} else {
add("std_alloc.bc")
}
}.map {
File(distribution.defaultNatives(target)).child(it).absolutePath
}
internal val launcherNativeLibraries: List<String> = distribution.launcherFiles.map {
File(distribution.defaultNatives(target)).child(it).absolutePath
}
internal val objCNativeLibrary: String =
File(distribution.defaultNatives(target)).child("objc.bc").absolutePath
internal val exceptionsSupportNativeLibrary: String =
File(distribution.defaultNatives(target)).child("exceptionsSupport.bc").absolutePath
internal val nativeLibraries: List<String> =
configuration.getList(KonanConfigKeys.NATIVE_LIBRARY_FILES)
internal val includeBinaries: List<String> =
configuration.getList(KonanConfigKeys.INCLUDED_BINARY_FILES)
internal val languageVersionSettings =
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)!!
internal val friendModuleFiles: Set<File> =
configuration.get(KonanConfigKeys.FRIEND_MODULES)?.map { File(it) }?.toSet() ?: emptySet()
internal val manifestProperties = configuration.get(KonanConfigKeys.MANIFEST_FILE)?.let {
File(it).loadProperties()
}
internal val isInteropStubs: Boolean get() = manifestProperties?.getProperty("interop") == "true"
}
fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String)
= this.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(priority, message)
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
class KonanConfigKeys {
companion object {
// Keep the list lexically sorted.
val CHECK_DEPENDENCIES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("check dependencies and download the missing ones")
val DEBUG: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("add debug information")
val FAKE_OVERRIDE_VALIDATOR: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("fake override validator")
val DISABLED_PHASES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("disable backend phases")
val BITCODE_EMBEDDING_MODE: CompilerConfigurationKey<BitcodeEmbedding.Mode>
= CompilerConfigurationKey.create("bitcode embedding mode")
val EMIT_LAZY_OBJC_HEADER_FILE: CompilerConfigurationKey<String?> =
CompilerConfigurationKey.create("output file to emit lazy Obj-C header")
val ENABLE_ASSERTIONS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("enable runtime assertions in generated code")
val ENABLED_PHASES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("enable backend phases")
val ENTRY: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("fully qualified main() name")
val EXPORTED_LIBRARIES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("libraries included into produced framework API")
val LIBRARIES_TO_CACHE: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("paths to libraries that to be compiled to cache")
val LIBRARY_TO_ADD_TO_CACHE: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create<String?>("path to library that to be added to cache")
val CACHE_DIRECTORIES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("paths to directories containing caches")
val CACHED_LIBRARIES: CompilerConfigurationKey<Map<String, String>>
= CompilerConfigurationKey.create<Map<String, String>>("mapping from library paths to cache paths")
val FRAMEWORK_IMPORT_HEADERS: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("headers imported to framework header")
val FRIEND_MODULES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create<List<String>>("friend module paths")
val GENERATE_TEST_RUNNER: CompilerConfigurationKey<TestRunnerKind>
= CompilerConfigurationKey.create("generate test runner")
val INCLUDED_BINARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("included binary file paths")
val KONAN_HOME: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("overridden compiler distribution path")
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("library file paths")
val LIBRARY_VERSION: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("library version")
val LIGHT_DEBUG: CompilerConfigurationKey<Boolean?>
= CompilerConfigurationKey.create("add light debug information")
val LINKER_ARGS: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("additional linker arguments")
val LIST_PHASES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("list backend phases")
val LIST_TARGETS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("list available targets")
val MANIFEST_FILE: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("provide manifest addend file")
val MEMORY_MODEL: CompilerConfigurationKey<MemoryModel>
= CompilerConfigurationKey.create("memory model")
val META_INFO: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("generate metadata")
val METADATA_KLIB: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("metadata klib")
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
= CompilerConfigurationKey.create("module kind")
val MODULE_NAME: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("module name")
val NATIVE_LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("native library file paths")
val NODEFAULTLIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with the default libraries")
val NOENDORSEDLIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with the endorsed libraries")
val NOMAIN: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("assume 'main' entry point to be provided by external libraries")
val NOSTDLIB: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with stdlib")
val NOPACK: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't the library into a klib file")
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("optimized compilation")
val OUTPUT: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("program or library name")
val OVERRIDE_CLANG_OPTIONS: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("arguments for clang")
val ALLOCATION_MODE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("allocation mode")
val PRINT_BITCODE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print bitcode")
val PRINT_DESCRIPTORS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print descriptors")
val PRINT_IR: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print ir")
val PRINT_IR_WITH_DESCRIPTORS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print ir with descriptors")
val PRINT_LOCATIONS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print locations")
val PRODUCE: CompilerConfigurationKey<CompilerOutputKind>
= CompilerConfigurationKey.create("compiler output kind")
val PURGE_USER_LIBS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("purge user-specified libs too")
val REPOSITORIES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("library search path repositories")
val RUNTIME_FILE: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("override default runtime file path")
val INCLUDED_LIBRARIES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey("klibs processed in the same manner as source files")
val SOURCE_MAP: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("generate source map")
val SHORT_MODULE_NAME: CompilerConfigurationKey<String?>
= CompilerConfigurationKey("short module name for IDE and export")
val STATIC_FRAMEWORK: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("produce a static library for a framework")
val TARGET: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("target we compile for")
val TEMPORARY_FILES_DIR: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("directory for temporary files")
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify bitcode")
val VERIFY_IR: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify IR")
val VERIFY_COMPILER: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify compiler")
val DEBUG_INFO_VERSION: CompilerConfigurationKey<Int>
= CompilerConfigurationKey.create("debug info format version")
val COVERAGE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("emit coverage info for sources")
val LIBRARIES_TO_COVER: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("libraries that should be covered")
val PROFRAW_PATH: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("path to *.profraw coverage output")
val OBJC_GENERICS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("write objc header with generics support")
val DEBUG_PREFIX_MAP: CompilerConfigurationKey<Map<String, String>>
= CompilerConfigurationKey.create("remap file source paths in debug info")
val PRE_LINK_CACHES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("perform compiler caches pre-link")
val OVERRIDE_KONAN_PROPERTIES: CompilerConfigurationKey<Map<String, String>>
= CompilerConfigurationKey.create("override konan.properties values")
val DESTROY_RUNTIME_MODE: CompilerConfigurationKey<DestroyRuntimeMode>
= CompilerConfigurationKey.create("when to destroy runtime")
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.utils.addToStdlib.cast
fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironment) {
val config = konanConfig.configuration
val targets = konanConfig.targetManager
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
targets.list()
}
val context = Context(konanConfig)
context.environment = environment
context.phaseConfig.konanPhasesConfig(konanConfig) // TODO: Wrong place to call it
if (konanConfig.infoArgsOnly) return
try {
toplevelPhase.cast<CompilerPhase<Context, Unit, Unit>>().invokeToplevel(context.phaseConfig, context, Unit)
} finally {
try {
context.disposeLlvm()
} finally {
context.freeNativeMem()
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
internal const val NATIVE_PTR_NAME = "NativePtr"
internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr"
internal const val VECTOR128 = "Vector128"
object KonanFqNames {
val function = FqName("kotlin.Function")
val kFunction = FqName("kotlin.reflect.KFunction")
val packageName = FqName("kotlin.native")
val internalPackageName = FqName("kotlin.native.internal")
val nativePtr = internalPackageName.child(Name.identifier(NATIVE_PTR_NAME)).toUnsafe()
val nonNullNativePtr = internalPackageName.child(Name.identifier(NON_NULL_NATIVE_PTR_NAME)).toUnsafe()
val Vector128 = packageName.child(Name.identifier(VECTOR128))
val throws = FqName("kotlin.Throws")
val cancellationException = FqName("kotlin.coroutines.cancellation.CancellationException")
val threadLocal = FqName("kotlin.native.concurrent.ThreadLocal")
val sharedImmutable = FqName("kotlin.native.concurrent.SharedImmutable")
val frozen = FqName("kotlin.native.internal.Frozen")
val leakDetectorCandidate = FqName("kotlin.native.internal.LeakDetectorCandidate")
val canBePrecreated = FqName("kotlin.native.internal.CanBePrecreated")
val typedIntrinsic = FqName("kotlin.native.internal.TypedIntrinsic")
val objCMethod = FqName("kotlinx.cinterop.ObjCMethod")
val hasFinalizer = FqName("kotlin.native.internal.HasFinalizer")
val hasFreezeHook = FqName("kotlin.native.internal.HasFreezeHook")
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.parseCompilerVersion
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.UnresolvedLibrary
import org.jetbrains.kotlin.library.resolver.impl.libraryResolver
import org.jetbrains.kotlin.library.toUnresolvedLibraries
import org.jetbrains.kotlin.util.Logger
import kotlin.system.exitProcess
class KonanLibrariesResolveSupport(
configuration: CompilerConfiguration,
target: KonanTarget,
distribution: Distribution
) {
private val includedLibraryFiles =
configuration.getList(KonanConfigKeys.INCLUDED_LIBRARIES).map { File(it) }
private val librariesToCacheFiles =
configuration.getList(KonanConfigKeys.LIBRARIES_TO_CACHE).map { File(it) } +
configuration.get(KonanConfigKeys.LIBRARY_TO_ADD_TO_CACHE).let {
if (it.isNullOrEmpty()) emptyList() else listOf(File(it))
}
private val libraryNames = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
private val unresolvedLibraries = libraryNames.toUnresolvedLibraries
private val repositories = configuration.getList(KonanConfigKeys.REPOSITORIES)
private val resolverLogger =
object : Logger {
private val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
override fun warning(message: String)= collector.report(CompilerMessageSeverity.STRONG_WARNING, message)
override fun error(message: String) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message)
override fun fatal(message: String): Nothing {
collector.report(CompilerMessageSeverity.ERROR, message)
(collector as? GroupingMessageCollector)?.flush()
exitProcess(1)
}
}
private val resolver = defaultResolver(
repositories,
libraryNames.filter { it.contains(File.separator) },
target,
distribution,
resolverLogger
).libraryResolver()
// We pass included libraries by absolute paths to avoid repository-based resolution for them.
// Strictly speaking such "direct" libraries should be specially handled by the resolver, not by KonanConfig.
// But currently the resolver is in the middle of a complex refactoring so it was decided to avoid changes in its logic.
// TODO: Handle included libraries in KonanLibraryResolver when it's refactored and moved into the big Kotlin repo.
internal val resolvedLibraries = run {
val additionalLibraryFiles = includedLibraryFiles + librariesToCacheFiles
resolver.resolveWithDependencies(
unresolvedLibraries + additionalLibraryFiles.map { UnresolvedLibrary(it.absolutePath, null) },
noStdLib = configuration.getBoolean(KonanConfigKeys.NOSTDLIB),
noDefaultLibs = configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS),
noEndorsedLibs = configuration.getBoolean(KonanConfigKeys.NOENDORSEDLIBS)
)
}
internal val exportedLibraries =
getExportedLibraries(configuration, resolvedLibraries, resolver.searchPathResolver, report = true)
internal val coveredLibraries =
getCoveredLibraries(configuration, resolvedLibraries, resolver.searchPathResolver)
internal val includedLibraries =
getIncludedLibraries(includedLibraryFiles, configuration, resolvedLibraries)
}
@@ -0,0 +1,388 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLambdasLowering
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
import org.jetbrains.kotlin.backend.konan.lower.StringConcatenationLowering
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
private val validateAll = false
private val filePhaseActions = if (validateAll) setOf(defaultDumper, ::fileValidationCallback) else setOf(defaultDumper)
private val modulePhaseActions = if (validateAll) setOf(defaultDumper, ::moduleValidationCallback) else setOf(defaultDumper)
private fun makeKonanFileLoweringPhase(
lowering: (Context) -> FileLoweringPass,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet()
) = makeIrFilePhase(lowering, name, description, prerequisite, actions = filePhaseActions)
private fun makeKonanModuleLoweringPhase(
lowering: (Context) -> FileLoweringPass,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet()
) = makeIrModulePhase(lowering, name, description, prerequisite, actions = modulePhaseActions)
internal fun makeKonanFileOpPhase(
op: (Context, IrFile) -> Unit,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet()
) = NamedCompilerPhase(
name, description, prerequisite, nlevels = 0,
lower = object : SameTypeCompilerPhase<Context, IrFile> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrFile>, context: Context, input: IrFile): IrFile {
op(context, input)
return input
}
},
actions = filePhaseActions
)
internal fun makeKonanModuleOpPhase(
op: (Context, IrModuleFragment) -> Unit,
name: String,
description: String,
prerequisite: Set<NamedCompilerPhase<Context, *>> = emptySet()
) = NamedCompilerPhase(
name, description, prerequisite, nlevels = 0,
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
op(context, input)
return input
}
},
actions = modulePhaseActions
)
internal val specialBackendChecksPhase = konanUnitPhase(
op = { irModule!!.files.forEach { SpecialBackendChecksTraversal(this).lower(it) } },
name = "SpecialBackendChecks",
description = "Special backend checks"
)
internal val removeExpectDeclarationsPhase = makeKonanModuleLoweringPhase(
::ExpectDeclarationsRemoving,
name = "RemoveExpectDeclarations",
description = "Expect declarations removing"
)
internal val stripTypeAliasDeclarationsPhase = makeKonanModuleLoweringPhase(
{ StripTypeAliasDeclarationsLowering() },
name = "StripTypeAliasDeclarations",
description = "Strip typealias declarations"
)
internal val lowerBeforeInlinePhase = makeKonanModuleLoweringPhase(
::PreInlineLowering,
name = "LowerBeforeInline",
description = "Special operations processing before inlining"
)
internal val arrayConstructorPhase = makeKonanModuleLoweringPhase(
::ArrayConstructorLowering,
name = "ArrayConstructor",
description = "Transform `Array(size) { index -> value }` into a loop"
)
internal val lateinitPhase = makeKonanModuleOpPhase(
{ context, irModule ->
NullableFieldsForLateinitCreationLowering(context).lower(irModule)
NullableFieldsDeclarationLowering(context).lower(irModule)
LateinitUsageLowering(context).lower(irModule)
},
name = "Lateinit",
description = "Lateinit properties lowering"
)
internal val propertyAccessorInlinePhase = makeKonanModuleLoweringPhase(
::PropertyAccessorInlineLowering,
name = "PropertyAccessorInline",
description = "Property accessor inline lowering"
)
internal val sharedVariablesPhase = makeKonanModuleLoweringPhase(
::SharedVariablesLowering,
name = "SharedVariables",
description = "Shared variable lowering",
prerequisite = setOf(lateinitPhase)
)
internal val extractLocalClassesFromInlineBodies = NamedCompilerPhase(
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
LocalClassesInInlineLambdasLowering(context).run {
input.files.forEach { lower(it) }
}
LocalClassesInInlineFunctionsLowering(context).run {
input.files.forEach { lower(it) }
}
LocalClassesExtractionFromInlineFunctionsLowering(context).run {
input.files.forEach { lower(it) }
}
return input
}
},
name = "ExtractLocalClassesFromInlineBodies",
description = "Extraction of local classes from inline bodies",
prerequisite = setOf(sharedVariablesPhase),
nlevels = 0,
actions = modulePhaseActions
)
internal val inlinePhase = NamedCompilerPhase(
lower = object : SameTypeCompilerPhase<Context, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
FunctionInlining(context, NativeInlineFunctionResolver(context)).run {
input.files.forEach { lower(it) }
}
return input
}
},
name = "Inline",
description = "Functions inlining",
prerequisite = setOf(lowerBeforeInlinePhase, arrayConstructorPhase, extractLocalClassesFromInlineBodies),
nlevels = 0,
actions = modulePhaseActions
)
internal val lowerAfterInlinePhase = makeKonanModuleOpPhase(
{ context, irModule ->
irModule.files.forEach(PostInlineLowering(context)::lower)
// TODO: Seems like this should be deleted in PsiToIR.
irModule.files.forEach(ContractsDslRemover(context)::lower)
},
name = "LowerAfterInline",
description = "Special operations processing after inlining"
)
/* IrFile phases */
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase (see kotlin: dd3f8ecaacd)
internal val provisionalFunctionExpressionPhase = makeKonanModuleLoweringPhase(
{ ProvisionalFunctionExpressionLowering() },
name = "FunctionExpression-before-inliner",
description = "Transform IrFunctionExpression to a local function reference"
)
internal val flattenStringConcatenationPhase = makeKonanFileLoweringPhase(
::FlattenStringConcatenationLowering,
name = "FlattenStringConcatenationLowering",
description = "Flatten nested string concatenation expressions into a single IrStringConcatenation"
)
internal val stringConcatenationPhase = makeKonanFileLoweringPhase(
::StringConcatenationLowering,
name = "StringConcatenation",
description = "String concatenation lowering"
)
internal val kotlinNothingValueExceptionPhase = makeKonanFileLoweringPhase(
::KotlinNothingValueExceptionLowering,
name = "KotlinNothingValueException",
description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'"
)
internal val enumConstructorsPhase = makeKonanFileLoweringPhase(
::EnumConstructorsLowering,
name = "EnumConstructors",
description = "Enum constructors lowering"
)
internal val initializersPhase = makeKonanFileLoweringPhase(
::InitializersLowering,
name = "Initializers",
description = "Initializers lowering",
prerequisite = setOf(enumConstructorsPhase)
)
internal val localFunctionsPhase = makeKonanFileOpPhase(
op = { context, irFile ->
LocalDelegatedPropertiesLowering().lower(irFile)
LocalDeclarationsLowering(context).lower(irFile)
LocalClassPopupLowering(context).lower(irFile)
},
name = "LocalFunctions",
description = "Local function lowering",
prerequisite = setOf(sharedVariablesPhase)
)
internal val tailrecPhase = makeKonanFileLoweringPhase(
::TailrecLowering,
name = "Tailrec",
description = "Tailrec lowering",
prerequisite = setOf(localFunctionsPhase)
)
internal val defaultParameterExtentPhase = makeKonanFileOpPhase(
{ context, irFile ->
KonanDefaultArgumentStubGenerator(context).lower(irFile)
DefaultParameterCleaner(context, replaceDefaultValuesWithStubs = true).lower(irFile)
KonanDefaultParameterInjector(context).lower(irFile)
},
name = "DefaultParameterExtent",
description = "Default parameter extent lowering",
prerequisite = setOf(tailrecPhase, enumConstructorsPhase)
)
internal val innerClassPhase = makeKonanFileLoweringPhase(
::InnerClassLowering,
name = "InnerClasses",
description = "Inner classes lowering",
prerequisite = setOf(defaultParameterExtentPhase)
)
internal val rangeContainsLoweringPhase = makeKonanFileLoweringPhase(
::RangeContainsLowering,
name = "RangeContains",
description = "Optimizes calls to contains() for ClosedRanges"
)
internal val forLoopsPhase = makeKonanFileLoweringPhase(
::ForLoopsLowering,
name = "ForLoops",
description = "For loops lowering"
)
internal val dataClassesPhase = makeKonanFileLoweringPhase(
::DataClassOperatorsLowering,
name = "DataClasses",
description = "Data classes lowering"
)
internal val finallyBlocksPhase = makeKonanFileLoweringPhase(
::FinallyBlocksLowering,
name = "FinallyBlocks",
description = "Finally blocks lowering",
prerequisite = setOf(initializersPhase, localFunctionsPhase, tailrecPhase)
)
internal val testProcessorPhase = makeKonanFileOpPhase(
{ context, irFile -> TestProcessor(context).process(irFile) },
name = "TestProcessor",
description = "Unit test processor"
)
internal val delegationPhase = makeKonanFileLoweringPhase(
::PropertyDelegationLowering,
name = "Delegation",
description = "Delegation lowering"
)
internal val functionReferencePhase = makeKonanFileLoweringPhase(
::FunctionReferenceLowering,
name = "FunctionReference",
description = "Function references lowering",
prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase`
)
internal val enumClassPhase = makeKonanFileOpPhase(
{ context, irFile -> EnumClassLowering(context).run(irFile) },
name = "Enums",
description = "Enum classes lowering",
prerequisite = setOf(enumConstructorsPhase, functionReferencePhase) // TODO: make weak dependency on `testProcessorPhase`
)
internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase(
::NativeSingleAbstractMethodLowering,
name = "SingleAbstractMethod",
description = "Replace SAM conversions with instances of interface-implementing classes",
prerequisite = setOf(functionReferencePhase)
)
internal val builtinOperatorPhase = makeKonanFileLoweringPhase(
::BuiltinOperatorLowering,
name = "BuiltinOperators",
description = "BuiltIn operators lowering",
prerequisite = setOf(defaultParameterExtentPhase, singleAbstractMethodPhase)
)
internal val interopPhase = makeKonanFileLoweringPhase(
::InteropLowering,
name = "Interop",
description = "Interop lowering",
prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase)
)
internal val varargPhase = makeKonanFileLoweringPhase(
::VarargInjectionLowering,
name = "Vararg",
description = "Vararg lowering",
prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase)
)
internal val compileTimeEvaluatePhase = makeKonanFileLoweringPhase(
::CompileTimeEvaluateLowering,
name = "CompileTimeEvaluate",
description = "Compile time evaluation lowering",
prerequisite = setOf(varargPhase)
)
internal val coroutinesPhase = makeKonanFileLoweringPhase(
::NativeSuspendFunctionsLowering,
name = "Coroutines",
description = "Coroutines lowering",
prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase)
)
internal val typeOperatorPhase = makeKonanFileLoweringPhase(
::TypeOperatorLowering,
name = "TypeOperators",
description = "Type operators lowering",
prerequisite = setOf(coroutinesPhase)
)
internal val bridgesPhase = makeKonanFileOpPhase(
{ context, irFile ->
BridgesBuilding(context).runOnFilePostfix(irFile)
WorkersBridgesBuilding(context).lower(irFile)
},
name = "Bridges",
description = "Bridges building",
prerequisite = setOf(coroutinesPhase)
)
internal val autoboxPhase = makeKonanFileLoweringPhase(
::Autoboxing,
name = "Autobox",
description = "Autoboxing of primitive types",
prerequisite = setOf(bridgesPhase, coroutinesPhase)
)
internal val returnsInsertionPhase = makeKonanFileLoweringPhase(
::ReturnsInsertionLowering,
name = "ReturnsInsertion",
description = "Returns insertion for Unit functions",
prerequisite = setOf(autoboxPhase, coroutinesPhase, enumClassPhase)
)
internal val ifNullExpressionsFusionPhase = makeKonanFileLoweringPhase(
::IfNullExpressionsFusionLowering,
name = "IfNullExpressionsFusionLowering",
description = "Simplify '?.' and '?:' operator chains"
)
internal val foldConstantLoweringPhase = makeKonanFileOpPhase(
{ context, irFile -> FoldConstantLowering(context).lower(irFile) },
name = "FoldConstantLowering",
description = "Constant Folding",
prerequisite = setOf(flattenStringConcatenationPhase)
)
internal val computeStringTrimPhase = makeKonanFileLoweringPhase(
::StringTrimLowering,
name = "StringTrimLowering",
description = "Compute trimIndent and trimMargin operations on constant strings"
)
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_REFLECT_FQ_NAME
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import kotlin.reflect.KProperty
class KonanReflectionTypes(module: ModuleDescriptor, internalPackage: FqName) {
private val kotlinReflectScope: MemberScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
module.getPackage(KOTLIN_REFLECT_FQ_NAME).memberScope
}
private val internalScope: MemberScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
module.getPackage(internalPackage).memberScope
}
private fun find(memberScope: MemberScope, className: String): ClassDescriptor {
val name = Name.identifier(className)
return memberScope.getContributedClassifier(name, NoLookupLocation.FROM_REFLECTION) as ClassDescriptor
}
private class ClassLookup(val memberScope: MemberScope) {
operator fun getValue(types: KonanReflectionTypes, property: KProperty<*>): ClassDescriptor {
return types.find(memberScope, property.name.capitalize())
}
}
fun getKFunction(n: Int): ClassDescriptor = find(kotlinReflectScope, "KFunction$n")
fun getKSuspendFunction(n: Int): ClassDescriptor = find(kotlinReflectScope, "KSuspendFunction$n")
val kProperty0: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty0: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty1: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty2: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kTypeProjection: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kType: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kVariance: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kFunctionImpl: ClassDescriptor by ClassLookup(internalScope)
val kSuspendFunctionImpl: ClassDescriptor by ClassLookup(internalScope)
val kProperty0Impl: ClassDescriptor by ClassLookup(internalScope)
val kProperty1Impl: ClassDescriptor by ClassLookup(internalScope)
val kProperty2Impl: ClassDescriptor by ClassLookup(internalScope)
val kMutableProperty0Impl: ClassDescriptor by ClassLookup(internalScope)
val kMutableProperty1Impl: ClassDescriptor by ClassLookup(internalScope)
val kMutableProperty2Impl: ClassDescriptor by ClassLookup(internalScope)
val kLocalDelegatedPropertyImpl: ClassDescriptor by ClassLookup(internalScope)
val kLocalDelegatedMutablePropertyImpl: ClassDescriptor by ClassLookup(internalScope)
val typeOf = kotlinReflectScope.getContributedFunctions(Name.identifier("typeOf"), NoLookupLocation.FROM_REFLECTION).single()
}
@@ -0,0 +1,238 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal fun determineLinkerOutput(context: Context): LinkerOutputKind =
when (context.config.produce) {
CompilerOutputKind.FRAMEWORK -> {
val staticFramework = context.config.produceStaticFramework
if (staticFramework) LinkerOutputKind.STATIC_LIBRARY else LinkerOutputKind.DYNAMIC_LIBRARY
}
CompilerOutputKind.DYNAMIC_CACHE,
CompilerOutputKind.DYNAMIC -> LinkerOutputKind.DYNAMIC_LIBRARY
CompilerOutputKind.STATIC_CACHE,
CompilerOutputKind.STATIC -> LinkerOutputKind.STATIC_LIBRARY
CompilerOutputKind.PROGRAM -> LinkerOutputKind.EXECUTABLE
else -> TODO("${context.config.produce} should not reach native linker stage")
}
// TODO: We have a Linker.kt file in the shared module.
internal class Linker(val context: Context) {
private val platform = context.config.platform
private val config = context.config.configuration
private val linkerOutput = determineLinkerOutput(context)
private val linker = platform.linker
private val target = context.config.target
private val optimize = context.shouldOptimize()
private val debug = context.config.debug || context.config.lightDebug
fun link(objectFiles: List<ObjectFile>) {
val nativeDependencies = context.llvm.nativeDependenciesToLink
val includedBinariesLibraries = if (context.config.produce.isCache) {
context.config.librariesToCache
} else {
nativeDependencies.filterNot { context.config.cachedLibraries.isLibraryCached(it) }
}
val includedBinaries = includedBinariesLibraries.map { (it as? KonanLibrary)?.includedPaths.orEmpty() }.flatten()
val libraryProvidedLinkerFlags = context.llvm.allNativeDependencies.map { it.linkerOpts }.flatten()
if (context.config.produce.isCache) {
context.config.outputFiles.tempCacheDirectory!!.mkdirs()
saveAdditionalInfoForCache()
}
runLinker(objectFiles, includedBinaries, libraryProvidedLinkerFlags)
renameOutput()
}
private fun saveAdditionalInfoForCache() {
saveCacheBitcodeDependencies()
}
private fun saveCacheBitcodeDependencies() {
val outputFiles = context.config.outputFiles
val bitcodeDependenciesFile = File(outputFiles.bitcodeDependenciesFile!!)
val bitcodeDependencies = context.config.resolvedLibraries
.getFullList(TopologicalLibraryOrder)
.filter {
require(it is KonanLibrary)
context.llvmImports.bitcodeIsUsed(it)
&& it !in context.config.cacheSupport.librariesToCache // Skip loops.
}.cast<List<KonanLibrary>>()
bitcodeDependenciesFile.writeLines(bitcodeDependencies.map { it.uniqueName })
}
private fun renameOutput() {
if (context.config.produce.isCache) {
val outputFiles = context.config.outputFiles
// For caches the output file is a directory. It might be created by someone else,
// We have to delete it in order to the next renaming operation to succeed.
java.io.File(outputFiles.mainFile).delete()
if (!java.io.File(outputFiles.tempCacheDirectory!!.absolutePath).renameTo(java.io.File(outputFiles.mainFile)))
outputFiles.tempCacheDirectory.deleteRecursively()
}
}
private fun asLinkerArgs(args: List<String>): List<String> {
if (linker.useCompilerDriverAsLinker) {
return args
}
val result = mutableListOf<String>()
for (arg in args) {
// If user passes compiler arguments to us - transform them to linker ones.
if (arg.startsWith("-Wl,")) {
result.addAll(arg.substring(4).split(','))
} else {
result.add(arg)
}
}
return result
}
private fun runLinker(objectFiles: List<ObjectFile>,
includedBinaries: List<String>,
libraryProvidedLinkerFlags: List<String>): ExecutableFile? {
val additionalLinkerArgs: List<String>
val executable: String
if (context.config.produce != CompilerOutputKind.FRAMEWORK) {
additionalLinkerArgs = if (target.family.isAppleFamily) {
when (context.config.produce) {
CompilerOutputKind.DYNAMIC_CACHE ->
listOf("-install_name", context.config.outputFiles.dynamicCacheInstallName)
else -> listOf("-dead_strip")
}
} else {
emptyList()
}
executable = context.config.outputFiles.nativeBinaryFile
} else {
val framework = File(context.config.outputFile)
val dylibName = framework.name.removeSuffix(".framework")
val dylibRelativePath = when (target.family) {
Family.IOS,
Family.TVOS,
Family.WATCHOS -> dylibName
Family.OSX -> "Versions/A/$dylibName"
else -> error(target)
}
additionalLinkerArgs = listOf("-dead_strip", "-install_name", "@rpath/${framework.name}/$dylibRelativePath")
val dylibPath = framework.child(dylibRelativePath)
dylibPath.parentFile.mkdirs()
executable = dylibPath.absolutePath
}
val needsProfileLibrary = context.coverage.enabled
val mimallocEnabled = config.get(KonanConfigKeys.ALLOCATION_MODE) == "mimalloc" &&
target.supportsMimallocAllocator()
val linkerInput = determineLinkerInput(objectFiles, linkerOutput)
try {
File(executable).delete()
val linkerArgs = asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
BitcodeEmbedding.getLinkerOptions(context.config) +
linkerInput.caches.dynamic +
libraryProvidedLinkerFlags + additionalLinkerArgs
val finalOutputCommands = linker.finalLinkCommands(
objectFiles = linkerInput.objectFiles,
executable = executable,
libraries = linker.linkStaticLibraries(includedBinaries) + linkerInput.caches.static,
linkerArgs = linkerArgs,
optimize = optimize,
debug = debug,
kind = linkerOutput,
outputDsymBundle = context.config.outputFiles.symbolicInfoFile,
needsProfileLibrary = needsProfileLibrary,
mimallocEnabled = mimallocEnabled)
(linkerInput.preLinkCommands + finalOutputCommands).forEach {
it.logWith(context::log)
it.execute()
}
} catch (e: KonanExternalToolFailure) {
val extraUserInfo =
if (linkerInput.cachingInvolved)
"""
Please try to disable compiler caches and rerun the build. To disable compiler caches, add the following line to the gradle.properties file in the project's root directory:
kotlin.native.cacheKind.${target.presetName}=none
Also, consider filing an issue with full Gradle log here: https://kotl.in/issue
""".trimIndent()
else ""
context.reportCompilationError("${e.toolName} invocation reported errors\n$extraUserInfo\n${e.message}")
}
return executable
}
private fun shouldPerformPreLink(caches: CachesToLink, linkerOutputKind: LinkerOutputKind): Boolean {
// Pre-link is only useful when producing static library. Otherwise its just a waste of time.
val isStaticLibrary = linkerOutputKind == LinkerOutputKind.STATIC_LIBRARY &&
context.config.produce.isFinalBinary
val enabled = context.config.cacheSupport.preLinkCaches
val nonEmptyCaches = caches.static.isNotEmpty()
return isStaticLibrary && enabled && nonEmptyCaches
}
private fun determineLinkerInput(objectFiles: List<ObjectFile>, linkerOutputKind: LinkerOutputKind): LinkerInput {
val caches = determineCachesToLink(context)
// Since we have several linker stages that involve caching,
// we should detect cache usage early to report errors correctly.
val cachingInvolved = caches.static.isNotEmpty() || caches.dynamic.isNotEmpty()
return when {
context.config.produce == CompilerOutputKind.STATIC_CACHE -> {
// Do not link static cache dependencies.
LinkerInput(objectFiles, CachesToLink(emptyList(), caches.dynamic), emptyList(), cachingInvolved)
}
shouldPerformPreLink(caches, linkerOutputKind) -> {
val preLinkResult = context.config.tempFiles.create("withStaticCaches", ".o").absolutePath
val preLinkCommands = linker.preLinkCommands(objectFiles + caches.static, preLinkResult)
LinkerInput(listOf(preLinkResult), CachesToLink(emptyList(), caches.dynamic), preLinkCommands, cachingInvolved)
}
else -> LinkerInput(objectFiles, caches, emptyList(), cachingInvolved)
}
}
}
private class LinkerInput(
val objectFiles: List<ObjectFile>,
val caches: CachesToLink,
val preLinkCommands: List<Command>,
val cachingInvolved: Boolean
)
private class CachesToLink(val static: List<String>, val dynamic: List<String>)
private fun determineCachesToLink(context: Context): CachesToLink {
val staticCaches = mutableListOf<String>()
val dynamicCaches = mutableListOf<String>()
context.llvm.allCachedBitcodeDependencies.forEach { library ->
val currentBinaryContainsLibrary = context.llvmModuleSpecification.containsLibrary(library)
val cache = context.config.cachedLibraries.getLibraryCache(library)
?: error("Library $library is expected to be cached")
// Consistency check. Generally guaranteed by implementation.
if (currentBinaryContainsLibrary)
error("Library ${library.libraryName} is found in both cache and current binary")
val list = when (cache.kind) {
CachedLibraries.Cache.Kind.DYNAMIC -> dynamicCaches
CachedLibraries.Cache.Kind.STATIC -> staticCaches
}
list += cache.path
}
return CachesToLink(static = staticCaches, dynamic = dynamicCaches)
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.library.KotlinLibrary
/**
* Defines what LLVM module should consist of.
*/
interface LlvmModuleSpecification {
val isFinal: Boolean
fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean
fun importsKotlinDeclarationsFromOtherSharedLibraries(): Boolean
fun containsLibrary(library: KotlinLibrary): Boolean
fun containsModule(module: ModuleDescriptor): Boolean
fun containsModule(module: IrModuleFragment): Boolean
fun containsDeclaration(declaration: IrDeclaration): Boolean
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.library.KotlinLibrary
internal abstract class LlvmModuleSpecificationBase(protected val cachedLibraries: CachedLibraries) : LlvmModuleSpecification {
override fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean =
cachedLibraries.hasStaticCaches // A bit conservative but still valid.
override fun importsKotlinDeclarationsFromOtherSharedLibraries(): Boolean =
cachedLibraries.hasDynamicCaches // A bit conservative but still valid.
override fun containsModule(module: IrModuleFragment): Boolean =
containsModule(module.descriptor)
override fun containsModule(module: ModuleDescriptor): Boolean =
module.konanLibrary.let { it == null || containsLibrary(it) }
override fun containsDeclaration(declaration: IrDeclaration): Boolean =
declaration.konanLibrary.let { it == null || containsLibrary(it) }
}
internal class DefaultLlvmModuleSpecification(cachedLibraries: CachedLibraries)
: LlvmModuleSpecificationBase(cachedLibraries) {
override val isFinal = true
override fun containsLibrary(library: KotlinLibrary): Boolean = !cachedLibraries.isLibraryCached(library)
}
internal class CacheLlvmModuleSpecification(
cachedLibraries: CachedLibraries,
private val librariesToCache: Set<KotlinLibrary>
) : LlvmModuleSpecificationBase(cachedLibraries) {
override val isFinal = false
override fun containsLibrary(library: KotlinLibrary): Boolean = library in librariesToCache
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
enum class MemoryModel {
STRICT,
RELAXED,
EXPERIMENTAL,
}
@@ -0,0 +1,335 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationValueOrNull
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValueOrNull
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.isPublicApi
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getPublicSignature
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.constants.BooleanValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal val interopPackageName = InteropFqNames.packageName
internal val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
internal val objCObjectIdSignature = getTopLevelPublicSignature(objCObjectFqName)
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
private val objCClassIdSignature = getTopLevelPublicSignature(objCClassFqName)
private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCProtocol"))
private val objCProtocolIdSignature = getTopLevelPublicSignature(objCProtocolFqName)
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor")
private val objCFactoryFqName = interopPackageName.child(Name.identifier("ObjCFactory"))
private val objcnamesForwardDeclarationsPackageName = Name.identifier("objcnames")
private fun getTopLevelPublicSignature(fqName: FqName): IdSignature.PublicSignature =
getPublicSignature(fqName.parent(), fqName.shortName().asString())
fun ClassDescriptor.isObjCClass(): Boolean =
this.containingDeclaration.fqNameSafe != interopPackageName &&
this.getAllSuperClassifiers().any { it.fqNameSafe == objCObjectFqName } // TODO: this is not cheap. Cache me!
fun KotlinType.isObjCObjectType(): Boolean =
(this.supertypes() + this).any { TypeUtils.getClassDescriptor(it)?.fqNameSafe == objCObjectFqName }
private fun IrClass.selfOrAnySuperClass(pred: (IrClass) -> Boolean): Boolean {
if (pred(this)) return true
return superTypes.any { it.classOrNull!!.owner.selfOrAnySuperClass(pred) }
}
internal fun IrClass.isObjCClass() = this.packageFqName != interopPackageName &&
selfOrAnySuperClass { it.symbol.isPublicApi && objCObjectIdSignature == it.symbol.signature }
fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() &&
this.parentsWithSelf.filterIsInstance<ClassDescriptor>().any {
it.annotations.findAnnotation(externalObjCClassFqName) != null
}
fun IrClass.isExternalObjCClass(): Boolean = this.isObjCClass() &&
(this as IrDeclaration).parentDeclarationsWithSelf.filterIsInstance<IrClass>().any {
it.annotations.hasAnnotation(externalObjCClassFqName)
}
fun ClassDescriptor.isObjCForwardDeclaration(): Boolean =
this.findPackage().fqName.startsWith(objcnamesForwardDeclarationsPackageName)
fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().any {
it.fqNameSafe == objCClassFqName
}
fun IrClass.isObjCMetaClass(): Boolean = selfOrAnySuperClass {
it.symbol.isPublicApi && objCClassIdSignature == it.symbol.signature
}
fun IrClass.isObjCProtocolClass(): Boolean =
symbol.isPublicApi && objCProtocolIdSignature == symbol.signature
fun ClassDescriptor.isObjCProtocolClass(): Boolean =
this.fqNameSafe == objCProtocolFqName
fun FunctionDescriptor.isObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() }
fun IrFunction.isObjCClassMethod() =
this.parent.let { it is IrClass && it.isObjCClass() }
fun FunctionDescriptor.isExternalObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isExternalObjCClass() }
internal fun IrFunction.isExternalObjCClassMethod() =
this.parent.let {it is IrClass && it.isExternalObjCClass()}
// Special case: methods from Kotlin Objective-C classes can be called virtually from bridges.
fun FunctionDescriptor.canObjCClassMethodBeCalledVirtually(overriddenDescriptor: FunctionDescriptor) =
overriddenDescriptor.isOverridable && this.kind.isReal && !this.isExternalObjCClassMethod()
internal fun IrFunction.canObjCClassMethodBeCalledVirtually(overridden: IrFunction) =
overridden.isOverridable && this.origin != IrDeclarationOrigin.FAKE_OVERRIDE && !this.isExternalObjCClassMethod()
fun ClassDescriptor.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExternalObjCClass()
fun IrClass.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExternalObjCClass()
data class ObjCMethodInfo(val selector: String,
val encoding: String,
val isStret: Boolean)
private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
assert (this.kind.isReal)
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
return objCMethodInfo(methodAnnotation)
}
private fun IrFunction.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
assert (this.isReal)
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
return objCMethodInfo(methodAnnotation)
}
private fun objCMethodInfo(annotation: AnnotationDescriptor) = ObjCMethodInfo(
selector = annotation.getStringValue("selector"),
encoding = annotation.getStringValue("encoding"),
isStret = annotation.getArgumentValueOrNull<Boolean>("isStret") ?: false
)
private fun objCMethodInfo(annotation: IrConstructorCall) = ObjCMethodInfo(
selector = annotation.getAnnotationStringValue("selector"),
encoding = annotation.getAnnotationStringValue("encoding"),
isStret = annotation.getAnnotationValueOrNull<Boolean>("isStret") ?: false
)
/**
* @param onlyExternal indicates whether to accept overriding methods from Kotlin classes
*/
private fun FunctionDescriptor.getObjCMethodInfo(onlyExternal: Boolean): ObjCMethodInfo? {
if (this.kind.isReal) {
this.decodeObjCMethodAnnotation()?.let { return it }
if (onlyExternal) {
return null
}
}
return overriddenDescriptors.firstNotNullResult { it.getObjCMethodInfo(onlyExternal) }
}
/**
* @param onlyExternal indicates whether to accept overriding methods from Kotlin classes
*/
private fun IrSimpleFunction.getObjCMethodInfo(onlyExternal: Boolean): ObjCMethodInfo? {
if (this.isReal) {
this.decodeObjCMethodAnnotation()?.let { return it }
if (onlyExternal) {
return null
}
}
return overriddenSymbols.firstNotNullResult { it.owner.getObjCMethodInfo(onlyExternal) }
}
fun FunctionDescriptor.getExternalObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = true)
fun IrFunction.getExternalObjCMethodInfo(): ObjCMethodInfo? = (this as? IrSimpleFunction)?.getObjCMethodInfo(onlyExternal = true)
fun FunctionDescriptor.getObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = false)
fun IrFunction.getObjCMethodInfo(): ObjCMethodInfo? = (this as? IrSimpleFunction)?.getObjCMethodInfo(onlyExternal = false)
fun IrFunction.isObjCBridgeBased(): Boolean {
assert(this.isReal)
return this.annotations.hasAnnotation(objCMethodFqName) ||
this.annotations.hasAnnotation(objCFactoryFqName) ||
this.annotations.hasAnnotation(objCConstructorFqName)
}
/**
* Describes method overriding rules for Objective-C methods.
*
* This class is applied at [org.jetbrains.kotlin.resolve.OverridingUtil] as configured with
* `META-INF/services/org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition` resource.
*/
class ObjCOverridabilityCondition : ExternalOverridabilityCondition {
override fun getContract() = ExternalOverridabilityCondition.Contract.BOTH
override fun isOverridable(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor?
): ExternalOverridabilityCondition.Result {
if (superDescriptor.name == subDescriptor.name) { // Slow path:
if (superDescriptor is FunctionDescriptor && subDescriptor is FunctionDescriptor) {
superDescriptor.getExternalObjCMethodInfo()?.let { superInfo ->
val subInfo = subDescriptor.getExternalObjCMethodInfo()
if (subInfo != null) {
// Overriding Objective-C method by Objective-C method in interop stubs.
// Don't even check method signatures:
return if (superInfo.selector == subInfo.selector) {
ExternalOverridabilityCondition.Result.OVERRIDABLE
} else {
ExternalOverridabilityCondition.Result.INCOMPATIBLE
}
} else {
// Overriding Objective-C method by Kotlin method.
if (!parameterNamesMatch(superDescriptor, subDescriptor)) {
return ExternalOverridabilityCondition.Result.INCOMPATIBLE
}
}
}
} else if (superDescriptor.isExternalObjCClassProperty() && subDescriptor.isExternalObjCClassProperty()) {
return ExternalOverridabilityCondition.Result.OVERRIDABLE
}
}
return ExternalOverridabilityCondition.Result.UNKNOWN
}
private fun CallableDescriptor.isExternalObjCClassProperty() = this is PropertyDescriptor &&
(this.containingDeclaration as? ClassDescriptor)?.isExternalObjCClass() == true
private fun parameterNamesMatch(first: FunctionDescriptor, second: FunctionDescriptor): Boolean {
// The original Objective-C method selector is represented as
// function name and parameter names (except first).
if (first.valueParameters.size != second.valueParameters.size) {
return false
}
first.valueParameters.forEachIndexed { index, parameter ->
if (index > 0 && parameter.name != second.valueParameters[index].name) {
return false
}
}
return true
}
}
fun IrConstructor.objCConstructorIsDesignated(): Boolean =
this.getAnnotationArgumentValue<Boolean>(objCConstructorFqName, "designated")
?: error("Could not find 'designated' argument")
fun ConstructorDescriptor.objCConstructorIsDesignated(): Boolean {
val annotation = this.annotations.findAnnotation(objCConstructorFqName)!!
val value = annotation.allValueArguments[Name.identifier("designated")]!!
return (value as BooleanValue).value
}
val IrConstructor.isObjCConstructor get() = this.annotations.hasAnnotation(objCConstructorFqName)
val ConstructorDescriptor.isObjCConstructor get() = this.annotations.hasAnnotation(objCConstructorFqName)
// TODO-DCE-OBJC-INIT: Selector should be preserved by DCE.
fun IrConstructor.getObjCInitMethod(): IrSimpleFunction? {
return this.annotations.findAnnotation(objCConstructorFqName)?.let {
val initSelector = it.getAnnotationStringValue("initSelector")
this.constructedClass.declarations.asSequence()
.filterIsInstance<IrSimpleFunction>()
.single { it.getExternalObjCMethodInfo()?.selector == initSelector }
}
}
fun ConstructorDescriptor.getObjCInitMethod(): FunctionDescriptor? {
return this.annotations.findAnnotation(objCConstructorFqName)?.let {
val initSelector = it.getAnnotationStringValue("initSelector")
val memberScope = constructedClass.unsubstitutedMemberScope
val functionNames = memberScope.getFunctionNames()
for (name in functionNames) {
val functions = memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
for (function in functions) {
val objectInfo = function.getExternalObjCMethodInfo() ?: continue
if (objectInfo.selector == initSelector) return function
}
}
error("Cannot find ObjInitMethod for $this")
}
}
val IrFunction.hasObjCFactoryAnnotation get() = this.annotations.hasAnnotation(objCFactoryFqName)
val FunctionDescriptor.hasObjCFactoryAnnotation get() = this.annotations.hasAnnotation(objCFactoryFqName)
val IrFunction.hasObjCMethodAnnotation get() = this.annotations.hasAnnotation(objCMethodFqName)
val FunctionDescriptor.hasObjCMethodAnnotation get() = this.annotations.hasAnnotation(objCMethodFqName)
fun FunctionDescriptor.getObjCFactoryInitMethodInfo(): ObjCMethodInfo? {
val factoryAnnotation = this.annotations.findAnnotation(objCFactoryFqName) ?: return null
return objCMethodInfo(factoryAnnotation)
}
fun IrFunction.getObjCFactoryInitMethodInfo(): ObjCMethodInfo? {
val factoryAnnotation = this.annotations.findAnnotation(objCFactoryFqName) ?: return null
return objCMethodInfo(factoryAnnotation)
}
fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.valueParameters.isEmpty()) {
descriptor.name.asString()
} else {
buildString {
append(descriptor.name)
append(':')
descriptor.valueParameters.drop(1).forEach {
append(it.name)
append(':')
}
}
}
fun ClassDescriptor.getExternalObjCClassBinaryName(): String =
this.getExplicitExternalObjCClassBinaryName()
?: this.name.asString()
fun ClassDescriptor.getExternalObjCMetaClassBinaryName(): String =
this.getExplicitExternalObjCClassBinaryName()
?: this.name.asString().removeSuffix("Meta")
private fun ClassDescriptor.getExplicitExternalObjCClassBinaryName() =
this.annotations.findAnnotation(externalObjCClassFqName)!!.getStringValueOrNull("binaryName")
@@ -0,0 +1,189 @@
package org.jetbrains.kotlin.backend.konan
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
import llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.makeVisibilityHiddenLikeLlvmInternalizePass
import org.jetbrains.kotlin.konan.target.*
private fun initializeLlvmGlobalPassRegistry() {
val passRegistry = LLVMGetGlobalPassRegistry()
LLVMInitializeCore(passRegistry)
LLVMInitializeTransformUtils(passRegistry)
LLVMInitializeScalarOpts(passRegistry)
LLVMInitializeVectorization(passRegistry)
LLVMInitializeInstCombine(passRegistry)
LLVMInitializeIPO(passRegistry)
LLVMInitializeInstrumentation(passRegistry)
LLVMInitializeAnalysis(passRegistry)
LLVMInitializeIPA(passRegistry)
LLVMInitializeCodeGen(passRegistry)
LLVMInitializeTarget(passRegistry)
}
internal fun shouldRunLateBitcodePasses(context: Context): Boolean {
return context.coverage.enabled
}
internal fun runLateBitcodePasses(context: Context, llvmModule: LLVMModuleRef) {
val passManager = LLVMCreatePassManager()!!
LLVMKotlinAddTargetLibraryInfoWrapperPass(passManager, context.llvm.targetTriple)
context.coverage.addLateLlvmPasses(passManager)
LLVMRunPassManager(passManager, llvmModule)
LLVMDisposePassManager(passManager)
}
private class LlvmPipelineConfiguration(context: Context) {
private val target = context.config.target
private val configurables: Configurables = context.config.platform.configurables
val targetTriple: String = context.llvm.targetTriple
val cpuModel: String = configurables.targetCpu ?: run {
context.reportCompilationWarning("targetCpu for target $target was not set. Targeting `generic` cpu.")
"generic"
}
val cpuFeatures: String = configurables.targetCpuFeatures ?: ""
/**
* Null value means that LLVM should use default inliner params
* for the provided optimization and size level.
*/
val customInlineThreshold: Int? = when {
context.shouldOptimize() -> configurables.llvmInlineThreshold?.let {
it.toIntOrNull() ?: run {
context.reportCompilationWarning(
"`llvmInlineThreshold` should be an integer. Got `$it` instead. Using default value."
)
null
}
}
context.shouldContainDebugInfo() -> null
else -> null
}
val optimizationLevel: LlvmOptimizationLevel = when {
context.shouldOptimize() -> LlvmOptimizationLevel.AGGRESSIVE
context.shouldContainDebugInfo() -> LlvmOptimizationLevel.NONE
else -> LlvmOptimizationLevel.DEFAULT
}
val sizeLevel: LlvmSizeLevel = when {
// We try to optimize code as much as possible on embedded targets.
target is KonanTarget.ZEPHYR ||
target == KonanTarget.WASM32 -> LlvmSizeLevel.AGGRESSIVE
context.shouldOptimize() -> LlvmSizeLevel.NONE
context.shouldContainDebugInfo() -> LlvmSizeLevel.NONE
else -> LlvmSizeLevel.NONE
}
val codegenOptimizationLevel: LLVMCodeGenOptLevel = when {
context.shouldOptimize() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive
context.shouldContainDebugInfo() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelNone
else -> LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault
}
val relocMode: LLVMRelocMode = configurables.currentRelocationMode(context).translateToLlvmRelocMode()
private fun RelocationModeFlags.Mode.translateToLlvmRelocMode() = when (this) {
RelocationModeFlags.Mode.PIC -> LLVMRelocMode.LLVMRelocPIC
RelocationModeFlags.Mode.STATIC -> LLVMRelocMode.LLVMRelocStatic
RelocationModeFlags.Mode.DEFAULT -> LLVMRelocMode.LLVMRelocDefault
}
val codeModel: LLVMCodeModel = LLVMCodeModel.LLVMCodeModelDefault
enum class LlvmOptimizationLevel(val value: Int) {
NONE(0),
DEFAULT(1),
AGGRESSIVE(3)
}
enum class LlvmSizeLevel(val value: Int) {
NONE(0),
DEFAULT(1),
AGGRESSIVE(2)
}
}
internal fun runLlvmOptimizationPipeline(context: Context) {
val llvmModule = context.llvmModule!!
val config = LlvmPipelineConfiguration(context)
context.log {
"""
Running LLVM optimizations with the following parameters:
target_triple: ${config.targetTriple}
cpu_model: ${config.cpuModel}
cpu_features: ${config.cpuFeatures}
optimization_level: ${config.optimizationLevel.value}
size_level: ${config.sizeLevel.value}
inline_threshold: ${config.customInlineThreshold ?: "default"}
""".trimIndent()
}
memScoped {
LLVMKotlinInitializeTargets()
initializeLlvmGlobalPassRegistry()
val passBuilder = LLVMPassManagerBuilderCreate()
val modulePasses = LLVMCreatePassManager()
LLVMPassManagerBuilderSetOptLevel(passBuilder, config.optimizationLevel.value)
LLVMPassManagerBuilderSetSizeLevel(passBuilder, config.sizeLevel.value)
// TODO: use LLVMGetTargetFromName instead.
val target = alloc<LLVMTargetRefVar>()
val foundLlvmTarget = LLVMGetTargetFromTriple(config.targetTriple, target.ptr, null) == 0
check(foundLlvmTarget) { "Cannot get target from triple ${config.targetTriple}." }
val targetMachine = LLVMCreateTargetMachine(
target.value,
config.targetTriple,
config.cpuModel,
config.cpuFeatures,
config.codegenOptimizationLevel,
config.relocMode,
config.codeModel)
LLVMKotlinAddTargetLibraryInfoWrapperPass(modulePasses, config.targetTriple)
// TargetTransformInfo pass.
LLVMAddAnalysisPasses(targetMachine, modulePasses)
if (context.llvmModuleSpecification.isFinal) {
// Since we are in a "closed world" internalization can be safely used
// to reduce size of a bitcode with global dce.
LLVMAddInternalizePass(modulePasses, 0)
} else if (context.config.produce == CompilerOutputKind.STATIC_CACHE) {
// Hidden visibility makes symbols internal when linking the binary.
// When producing dynamic library, this enables stripping unused symbols from binary with -dead_strip flag,
// similar to DCE enabled by internalize but later:
makeVisibilityHiddenLikeLlvmInternalizePass(llvmModule)
// Important for binary size, workarounds references to undefined symbols from interop libraries.
}
LLVMAddGlobalDCEPass(modulePasses)
config.customInlineThreshold?.let { threshold ->
LLVMPassManagerBuilderUseInlinerWithThreshold(passBuilder, threshold)
}
// Pipeline that is similar to `llvm-lto`.
// TODO: Add ObjC optimization passes.
LLVMPassManagerBuilderPopulateLTOPassManager(passBuilder, modulePasses, Internalize = 0, RunInliner = 1)
LLVMRunPassManager(modulePasses, llvmModule)
LLVMPassManagerBuilderDispose(passBuilder)
LLVMDisposeTargetMachine(targetMachine)
LLVMDisposePassManager(modulePasses)
}
if (shouldRunLateBitcodePasses(context)) {
runLateBitcodePasses(context, llvmModule)
}
}
internal fun RelocationModeFlags.currentRelocationMode(context: Context): RelocationModeFlags.Mode =
when (determineLinkerOutput(context)) {
LinkerOutputKind.DYNAMIC_LIBRARY -> dynamicLibraryRelocationMode
LinkerOutputKind.STATIC_LIBRARY -> staticLibraryRelocationMode
LinkerOutputKind.EXECUTABLE -> executableRelocationMode
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.util.prefixBaseNameIfNot
import org.jetbrains.kotlin.util.removeSuffixIfPresent
import org.jetbrains.kotlin.util.suffixIfNot
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.visibleName
import kotlin.random.Random
/**
* Creates and stores terminal compiler outputs.
*/
class OutputFiles(outputPath: String?, target: KonanTarget, val produce: CompilerOutputKind) {
private val prefix = produce.prefix(target)
private val suffix = produce.suffix(target)
val outputName = outputPath?.removeSuffixIfPresent(suffix) ?: produce.visibleName
fun klibOutputFileName(isPacked: Boolean): String =
if (isPacked) "$outputName$suffix" else outputName
/**
* Header file for dynamic library.
*/
val cAdapterHeader by lazy { File("${outputName}_api.h") }
val cAdapterDef by lazy { File("${outputName}.def") }
/**
* Main compiler's output file.
*/
val mainFile =
if (produce.isCache)
outputName
else
outputName.fullOutputName()
private val cacheFile = File(outputName.fullOutputName()).absoluteFile.name
val dynamicCacheInstallName = File(outputName).child(cacheFile).absolutePath
val tempCacheDirectory =
if (produce.isCache)
File(outputName + Random.nextLong().toString())
else null
val nativeBinaryFile =
if (produce.isCache)
tempCacheDirectory!!.child(cacheFile).absolutePath
else mainFile
val symbolicInfoFile = "$nativeBinaryFile.dSYM"
val bitcodeDependenciesFile =
if (produce.isCache)
tempCacheDirectory!!.child(CachedLibraries.BITCODE_DEPENDENCIES_FILE_NAME).absolutePath
else null
private fun String.fullOutputName() = prefixBaseNameIfNeeded(prefix).suffixIfNeeded(suffix)
private fun String.prefixBaseNameIfNeeded(prefix: String) =
if (produce.isCache)
prefixBaseNameAlways(prefix)
else prefixBaseNameIfNot(prefix)
private fun String.suffixIfNeeded(prefix: String) =
if (produce.isCache)
suffixAlways(prefix)
else suffixIfNot(prefix)
private fun String.prefixBaseNameAlways(prefix: String): String {
val file = File(this).absoluteFile
val name = file.name
val directory = file.parent
return "$directory/$prefix$name"
}
private fun String.suffixAlways(suffix: String) = "$this$suffix"
}
@@ -0,0 +1,215 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.ManglerChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.Ir2DescriptorManglerAdapter
import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.DeclarationStubGeneratorImpl
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CleanableBindingContext
import org.jetbrains.kotlin.utils.DFS
internal fun Context.psiToIr(
symbolTable: SymbolTable,
isProducingLibrary: Boolean,
useLinkerWhenProducingLibrary: Boolean
) {
// Translate AST to high level IR.
val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER)?:false
val messageLogger = config.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
val translator = Psi2IrTranslator(config.configuration.languageVersionSettings, Psi2IrConfiguration(false))
val generatorContext = translator.createGeneratorContext(moduleDescriptor, bindingContext, symbolTable)
val pluginExtensions = IrGenerationExtension.getInstances(config.project)
val forwardDeclarationsModuleDescriptor = moduleDescriptor.allDependencyModules.firstOrNull { it.isForwardDeclarationModule }
val modulesWithoutDCE = moduleDescriptor.allDependencyModules
.filter { !llvmModuleSpecification.isFinal && llvmModuleSpecification.containsModule(it) }
// Note: using [llvmModuleSpecification] since this phase produces IR for generating single LLVM module.
val exportedDependencies = (getExportedDependencies() + modulesWithoutDCE).distinct()
val functionIrClassFactory = BuiltInFictitiousFunctionIrClassFactory(
symbolTable, generatorContext.irBuiltIns, reflectionTypes)
generatorContext.irBuiltIns.functionFactory = functionIrClassFactory
val stubGenerator = DeclarationStubGeneratorImpl(
moduleDescriptor, symbolTable,
config.configuration.languageVersionSettings
)
val symbols = KonanSymbols(this, generatorContext.irBuiltIns, symbolTable, symbolTable.lazyWrapper, functionIrClassFactory)
val irDeserializer = if (isProducingLibrary && !useLinkerWhenProducingLibrary) {
// Enable lazy IR generation for newly-created symbols inside BE
stubGenerator.unboundSymbolGeneration = true
object : IrDeserializer {
override fun getDeclaration(symbol: IrSymbol) = stubGenerator.getDeclaration(symbol)
override fun resolveBySignatureInModule(signature: IdSignature, kind: IrDeserializer.TopLevelSymbolKind, moduleName: Name): IrSymbol {
error("Should not be called")
}
}
} else {
val irProviderForCEnumsAndCStructs =
IrProviderForCEnumAndCStructStubs(generatorContext, interopBuiltIns, symbols)
val translationContext = object : TranslationPluginContext {
override val moduleDescriptor: ModuleDescriptor
get() = generatorContext.moduleDescriptor
override val symbolTable: ReferenceSymbolTable
get() = symbolTable
override val typeTranslator: TypeTranslator
get() = generatorContext.typeTranslator
override val irBuiltIns: IrBuiltIns
get() = generatorContext.irBuiltIns
}
KonanIrLinker(
moduleDescriptor,
functionIrClassFactory,
translationContext,
messageLogger,
generatorContext.irBuiltIns,
symbolTable,
forwardDeclarationsModuleDescriptor,
stubGenerator,
irProviderForCEnumsAndCStructs,
exportedDependencies,
config.cachedLibraries
).also { linker ->
// context.config.librariesWithDependencies could change at each iteration.
var dependenciesCount = 0
while (true) {
// context.config.librariesWithDependencies could change at each iteration.
val dependencies = moduleDescriptor.allDependencyModules.filter {
config.librariesWithDependencies(moduleDescriptor).contains(it.konanLibrary)
}
fun sortDependencies(dependencies: List<ModuleDescriptor>): Collection<ModuleDescriptor> {
return DFS.topologicalOrder(dependencies) {
it.allDependencyModules
}.reversed()
}
for (dependency in sortDependencies(dependencies).filter { it != moduleDescriptor }) {
val kotlinLibrary = dependency.getCapability(KlibModuleOrigin.CAPABILITY)?.let {
(it as? DeserializedKlibModuleOrigin)?.library
}
when {
isProducingLibrary -> linker.deserializeOnlyHeaderModule(dependency, kotlinLibrary)
kotlinLibrary != null && config.cachedLibraries.isLibraryCached(kotlinLibrary) ->
linker.deserializeHeadersWithInlineBodies(dependency, kotlinLibrary)
else -> linker.deserializeIrModuleHeader(dependency, kotlinLibrary)
}
}
if (dependencies.size == dependenciesCount) break
dependenciesCount = dependencies.size
}
// We need to run `buildAllEnumsAndStructsFrom` before `generateModuleFragment` because it adds references to symbolTable
// that should be bound.
modulesWithoutDCE
.filter(ModuleDescriptor::isFromInteropLibrary)
.forEach(irProviderForCEnumsAndCStructs::referenceAllEnumsAndStructsFrom)
translator.addPostprocessingStep {
irProviderForCEnumsAndCStructs.generateBodies()
}
}
}
translator.addPostprocessingStep { module ->
val pluginContext = IrPluginContextImpl(
generatorContext.moduleDescriptor,
generatorContext.bindingContext,
generatorContext.languageVersionSettings,
generatorContext.symbolTable,
generatorContext.typeTranslator,
generatorContext.irBuiltIns,
linker = irDeserializer,
diagnosticReporter = messageLogger
)
pluginExtensions.forEach { extension ->
extension.generate(module, pluginContext)
}
}
expectDescriptorToSymbol = mutableMapOf()
val mainModule = translator.generateModuleFragment(
generatorContext,
environment.getSourceFiles(),
irProviders = listOf(irDeserializer),
linkerExtensions = pluginExtensions,
// TODO: This is a hack to allow platform libs to build in reasonable time.
// referenceExpectsForUsedActuals() appears to be quadratic in time because of
// how ExpectedActualResolver is implemented.
// Need to fix ExpectActualResolver to either cache expects or somehow reduce the member scope searches.
expectDescriptorToSymbol = if (expectActualLinker) expectDescriptorToSymbol else null
).toKonanModule()
irDeserializer.postProcess()
// Enable lazy IR genration for newly-created symbols inside BE
stubGenerator.unboundSymbolGeneration = true
symbolTable.noUnboundLeft("Unbound symbols left after linker")
mainModule.acceptVoid(ManglerChecker(KonanManglerIr, Ir2DescriptorManglerAdapter(KonanManglerDesc)))
val modules = if (isProducingLibrary) emptyMap() else (irDeserializer as KonanIrLinker).modules
if (config.configuration.getBoolean(KonanConfigKeys.FAKE_OVERRIDE_VALIDATOR)) {
val fakeOverrideChecker = FakeOverrideChecker(KonanManglerIr, KonanManglerDesc)
modules.values.forEach { fakeOverrideChecker.check(it) }
}
irModule = mainModule
// Note: coupled with [shouldLower] below.
irModules = modules.filterValues { llvmModuleSpecification.containsModule(it) }
ir.symbols = symbols
if (!isProducingLibrary) {
if (this.stdlibModule in modulesWithoutDCE)
functionIrClassFactory.buildAllClasses()
internalAbi.init(irModules.values + irModule!!)
functionIrClassFactory.module = (modules.values + irModule!!).single { it.descriptor.isNativeStdlib() }
}
mainModule.files.forEach { it.metadata = KonanFileMetadataSource(mainModule) }
modules.values.forEach { module ->
module.files.forEach { it.metadata = KonanFileMetadataSource(module as KonanIrModuleFragmentImpl) }
}
val originalBindingContext = bindingContext as? CleanableBindingContext
?: error("BindingContext should be cleanable in K/N IR to avoid leaking memory: $bindingContext")
originalBindingContext.clear()
this.bindingContext = BindingContext.EMPTY
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.render
internal fun CommonBackendContext.reportCompilationError(message: String, irFile: IrFile, irElement: IrElement): Nothing {
report(irElement, irFile, message, true)
throw KonanCompilationException()
}
internal fun CommonBackendContext.reportCompilationError(message: String): Nothing {
report(null, null, message, true)
throw KonanCompilationException()
}
internal fun CompilerConfiguration.reportCompilationError(message: String): Nothing {
report(CompilerMessageSeverity.ERROR, message)
throw KonanCompilationException()
}
internal fun CommonBackendContext.reportCompilationWarning(message: String) {
report(null, null, message, false)
}
internal fun error(irFile: IrFile?, element: IrElement?, message: String): Nothing {
error(renderCompilerError(irFile, element, message))
}
internal fun renderCompilerError(irFile: IrFile?, element: IrElement?, message: String) =
buildString {
append("Internal compiler error: $message\n")
if (element == null) {
append("(IR element is null)")
} else {
if (irFile != null) {
val location = element.getCompilerMessageLocation(irFile)
append("at $location\n")
}
val renderedElement = try {
element.render()
} catch (e: Throwable) {
"(unable to render IR element)"
}
append(renderedElement)
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
object RuntimeNames {
val symbolNameAnnotation = FqName("kotlin.native.SymbolName")
val cnameAnnotation = FqName("kotlin.native.CName")
val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
val exportForCppRuntime = FqName("kotlin.native.internal.ExportForCppRuntime")
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
val cCall = FqName("kotlinx.cinterop.internal.CCall")
val cStructMemberAt = FqName("kotlinx.cinterop.internal.CStruct.MemberAt")
val cStructArrayMemberAt = FqName("kotlinx.cinterop.internal.CStruct.ArrayMemberAt")
val cStructBitField = FqName("kotlinx.cinterop.internal.CStruct.BitField")
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
val independent = FqName("kotlin.native.internal.Independent")
val filterExceptions = FqName("kotlin.native.internal.FilterExceptions")
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
val typedIntrinsicAnnotation = FqName("kotlin.native.internal.TypedIntrinsic")
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
enum class TestRunnerKind {
NONE,
MAIN_THREAD,
WORKER,
MAIN_THREAD_NO_EXIT
}
@@ -0,0 +1,135 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.context.MutableModuleContextImpl
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.metadata.NativeTypeTransformer
import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.serialization.konan.KotlinResolvedModuleDescriptors
import org.jetbrains.kotlin.storage.StorageManager
internal object TopDownAnalyzerFacadeForKonan {
fun analyzeFiles(files: Collection<KtFile>, context: Context): AnalysisResult {
val config = context.config
val moduleName = Name.special("<${config.moduleId}>")
val projectContext = ProjectContext(config.project, "TopDownAnalyzer for Konan")
val module = NativeFactories.DefaultDescriptorFactory.createDescriptorAndNewBuiltIns(
moduleName, projectContext.storageManager, origin = CurrentKlibModuleOrigin)
val moduleContext = MutableModuleContextImpl(module, projectContext)
val resolvedDependencies = ResolvedDependencies(
config.resolvedLibraries,
projectContext.storageManager,
module.builtIns,
config.languageVersionSettings,
config.friendModuleFiles,
module
)
val additionalPackages = mutableListOf<PackageFragmentProvider>()
if (!module.isNativeStdlib()) {
val dependencies = listOf(module) + resolvedDependencies.moduleDescriptors.resolvedDescriptors + resolvedDependencies.moduleDescriptors.forwardDeclarationsModule
module.setDependencies(dependencies, resolvedDependencies.friends)
} else {
assert (resolvedDependencies.moduleDescriptors.resolvedDescriptors.isEmpty())
moduleContext.setDependencies(module)
// [K][Suspend]FunctionN belong to stdlib.
additionalPackages += functionInterfacePackageFragmentProvider(projectContext.storageManager, module)
}
return analyzeFilesWithGivenTrace(files, BindingTraceContext(), moduleContext, context, additionalPackages)
}
fun analyzeFilesWithGivenTrace(
files: Collection<KtFile>,
trace: BindingTrace,
moduleContext: ModuleContext,
context: Context,
additionalPackages: List<PackageFragmentProvider> = emptyList()
): AnalysisResult {
// we print out each file we compile if frontend phase is verbose
files.takeIf {
frontendPhase in context.phaseConfig.verbose
} ?.forEach(::println)
val analyzerForKonan = createTopDownAnalyzerProviderForKonan(
moduleContext, trace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
context.config.configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS)!!,
additionalPackages
) {
initContainer(context.config)
}.apply {
postprocessComponents(context, files)
}.get<LazyTopDownAnalyzer>()
analyzerForKonan.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
return AnalysisResult.success(trace.bindingContext, moduleContext.module)
}
fun checkForErrors(files: Collection<KtFile>, bindingContext: BindingContext) {
AnalyzingUtils.throwExceptionOnErrors(bindingContext)
for (file in files) {
AnalyzingUtils.checkForSyntacticErrors(file)
}
}
}
private class ResolvedDependencies(
resolvedLibraries: KotlinLibraryResolveResult,
storageManager: StorageManager,
builtIns: KotlinBuiltIns,
specifics: LanguageVersionSettings,
friendModuleFiles: Set<File>,
currentModuleDescriptor: ModuleDescriptorImpl
) {
val moduleDescriptors: KotlinResolvedModuleDescriptors
val friends: Set<ModuleDescriptorImpl>
init {
val collectedFriends = mutableListOf<ModuleDescriptorImpl>()
val customAction: (KotlinLibrary, ModuleDescriptorImpl) -> Unit = { library, moduleDescriptor ->
if (friendModuleFiles.contains(library.libraryFile)) {
collectedFriends.add(moduleDescriptor)
}
}
this.moduleDescriptors = NativeFactories.DefaultResolvedDescriptorsFactory.createResolved(
resolvedLibraries, storageManager, builtIns, specifics, customAction, listOf(currentModuleDescriptor))
this.friends = collectedFriends.toSet()
}
}
val NativeFactories = KlibMetadataFactories(::KonanBuiltIns, NullFlexibleTypeDeserializer, NativeTypeTransformer())
@@ -0,0 +1,488 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.CheckDeclarationParentsVisitor
import org.jetbrains.kotlin.backend.common.IrValidator
import org.jetbrains.kotlin.backend.common.IrValidatorConfig
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irGetObjectValue
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) {
if (!context.config.needVerifyIr) return
val validatorConfig = IrValidatorConfig(
abortOnError = false,
ensureAllNodesAreDifferent = true,
checkTypes = true,
checkDescriptors = false
)
try {
module.accept(IrValidator(context, validatorConfig), null)
module.accept(CheckDeclarationParentsVisitor, null)
} catch (t: Throwable) {
// TODO: Add reference to source.
if (validatorConfig.abortOnError)
throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t)
else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}")
}
}
internal fun fileValidationCallback(state: ActionState, irFile: IrFile, context: Context) {
val validatorConfig = IrValidatorConfig(
abortOnError = false,
ensureAllNodesAreDifferent = true,
checkTypes = true,
checkDescriptors = false
)
try {
irFile.accept(IrValidator(context, validatorConfig), null)
irFile.accept(CheckDeclarationParentsVisitor, null)
} catch (t: Throwable) {
// TODO: Add reference to source.
if (validatorConfig.abortOnError)
throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t)
else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}")
}
}
internal fun konanUnitPhase(
name: String,
description: String,
prerequisite: Set<AnyNamedPhase> = emptySet(),
op: Context.() -> Unit
) = namedOpUnitPhase(name, description, prerequisite, op)
internal val frontendPhase = konanUnitPhase(
op = {
val environment = environment
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector,
environment.configuration.languageVersionSettings)
// Build AST and binding info.
analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles()) {
TopDownAnalyzerFacadeForKonan.analyzeFiles(environment.getSourceFiles(), this)
}
if (analyzerWithCompilerReport.hasErrors()) {
throw KonanCompilationException()
}
moduleDescriptor = analyzerWithCompilerReport.analysisResult.moduleDescriptor
bindingContext = analyzerWithCompilerReport.analysisResult.bindingContext
},
name = "Frontend",
description = "Frontend builds AST"
)
/**
* Valid from [createSymbolTablePhase] until [destroySymbolTablePhase].
*/
private var Context.symbolTable: SymbolTable? by Context.nullValue()
internal val createSymbolTablePhase = konanUnitPhase(
op = {
this.symbolTable = SymbolTable(KonanIdSignaturer(KonanManglerDesc), IrFactoryImpl)
},
name = "CreateSymbolTable",
description = "Create SymbolTable"
)
internal val objCExportPhase = konanUnitPhase(
op = {
objCExport = ObjCExport(this, symbolTable!!)
},
name = "ObjCExport",
description = "Objective-C header generation",
prerequisite = setOf(createSymbolTablePhase)
)
internal val buildCExportsPhase = konanUnitPhase(
op = {
if (this.isNativeLibrary) {
this.cAdapterGenerator = CAdapterGenerator(this).also {
it.buildExports(this.symbolTable!!)
}
}
},
name = "BuildCExports",
description = "Build C exports",
prerequisite = setOf(createSymbolTablePhase)
)
internal val psiToIrPhase = konanUnitPhase(
op = {
this.psiToIr(symbolTable!!,
isProducingLibrary = config.produce == CompilerOutputKind.LIBRARY,
useLinkerWhenProducingLibrary = false)
},
name = "Psi2Ir",
description = "Psi to IR conversion and klib linkage",
prerequisite = setOf(createSymbolTablePhase)
)
// Coupled with [psiToIrPhase] logic above.
internal fun shouldLower(context: Context, declaration: IrDeclaration): Boolean {
return context.llvmModuleSpecification.containsDeclaration(declaration)
}
internal val destroySymbolTablePhase = konanUnitPhase(
op = {
this.symbolTable = null // TODO: invalidate symbolTable itself.
},
name = "DestroySymbolTable",
description = "Destroy SymbolTable",
prerequisite = setOf(createSymbolTablePhase)
)
// TODO: We copy default value expressions from expects to actuals before IR serialization,
// because the current infrastructure doesn't allow us to get them at deserialization stage.
// That requires some design and implementation work.
internal val copyDefaultValuesToActualPhase = konanUnitPhase(
op = {
ExpectToActualDefaultValueCopier(irModule!!).process()
},
name = "CopyDefaultValuesToActual",
description = "Copy default values from expect to actual declarations"
)
internal val serializerPhase = konanUnitPhase(
op = {
val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false
val messageLogger = config.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
serializedIr = irModule?.let { ir ->
KonanIrModuleSerializer(
messageLogger, ir.irBuiltins, expectDescriptorToSymbol, skipExpects = !expectActualLinker
).serializedIrModule(ir)
}
val serializer = KlibMetadataMonolithicSerializer(
this.config.configuration.languageVersionSettings,
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)!!,
config.project,
!expectActualLinker, includeOnlyModuleContent = true)
serializedMetadata = serializer.serializeModule(moduleDescriptor)
},
name = "Serializer",
description = "Serialize descriptor tree and inline IR bodies"
)
internal val objectFilesPhase = konanUnitPhase(
op = { compilerOutput = BitcodeCompiler(this).makeObjectFiles(bitcodeFileName) },
name = "ObjectFiles",
description = "Bitcode to object file"
)
internal val linkerPhase = konanUnitPhase(
op = { Linker(this).link(compilerOutput) },
name = "Linker",
description = "Linker"
)
internal val allLoweringsPhase = NamedCompilerPhase(
name = "IrLowering",
description = "IR Lowering",
// TODO: The lowerings before inlinePhase should be aligned with [NativeInlineFunctionResolver.kt]
lower = removeExpectDeclarationsPhase then
stripTypeAliasDeclarationsPhase then
lowerBeforeInlinePhase then
arrayConstructorPhase then
lateinitPhase then
sharedVariablesPhase then
extractLocalClassesFromInlineBodies then
inlinePhase then
provisionalFunctionExpressionPhase then
lowerAfterInlinePhase then
performByIrFile(
name = "IrLowerByFile",
description = "IR Lowering by file",
lower = listOf(
rangeContainsLoweringPhase,
forLoopsPhase,
flattenStringConcatenationPhase,
foldConstantLoweringPhase,
computeStringTrimPhase,
stringConcatenationPhase,
enumConstructorsPhase,
initializersPhase,
localFunctionsPhase,
tailrecPhase,
defaultParameterExtentPhase,
innerClassPhase,
dataClassesPhase,
ifNullExpressionsFusionPhase,
testProcessorPhase,
delegationPhase,
functionReferencePhase,
singleAbstractMethodPhase,
builtinOperatorPhase,
finallyBlocksPhase,
enumClassPhase,
interopPhase,
varargPhase,
compileTimeEvaluatePhase,
kotlinNothingValueExceptionPhase,
coroutinesPhase,
typeOperatorPhase,
bridgesPhase,
autoboxPhase,
returnsInsertionPhase,
)
),
actions = setOf(defaultDumper, ::moduleValidationCallback)
)
internal val dependenciesLowerPhase = NamedCompilerPhase(
name = "LowerLibIR",
description = "Lower library's IR",
prerequisite = emptySet(),
lower = object : CompilerPhase<Context, IrModuleFragment, IrModuleFragment> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<IrModuleFragment>, context: Context, input: IrModuleFragment): IrModuleFragment {
val files = mutableListOf<IrFile>()
files += input.files
input.files.clear()
// TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order.
context.librariesWithDependencies
.reversed()
.forEach {
val libModule = context.irModules[it.libraryName]
?: return@forEach
input.files += libModule.files
allLoweringsPhase.invoke(phaseConfig, phaserState, context, input)
input.files.clear()
}
// Save all files for codegen in reverse topological order.
// This guarantees that libraries initializers are emitted in correct order.
context.librariesWithDependencies
.forEach {
val libModule = context.irModules[it.libraryName]
?: return@forEach
input.files += libModule.files
}
input.files += files
return input
}
})
internal val entryPointPhase = makeCustomPhase<Context, IrModuleFragment>(
name = "addEntryPoint",
description = "Add entry point for program",
prerequisite = emptySet(),
op = { context, _ ->
assert(context.config.produce == CompilerOutputKind.PROGRAM)
val originalFile = context.ir.symbols.entryPoint!!.owner.file
val originalModule = originalFile.packageFragmentDescriptor.containingDeclaration
val file = if (context.llvmModuleSpecification.containsModule(originalModule)) {
originalFile
} else {
// `main` function is compiled to other LLVM module.
// For example, test running support uses `main` defined in stdlib.
context.irModule!!.addFile(originalFile.fileEntry, originalFile.fqName)
}
require(context.llvmModuleSpecification.containsModule(
file.packageFragmentDescriptor.containingDeclaration))
file.addChild(makeEntryPoint(context))
}
)
internal val exportInternalAbiPhase = makeKonanModuleOpPhase(
name = "exportInternalAbi",
description = "Add accessors to private entities",
prerequisite = emptySet(),
op = { context, module ->
val visitor = object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
if (declaration.isCompanion) {
val function = context.irFactory.buildFun {
name = InternalAbi.getCompanionObjectAccessorName(declaration)
origin = InternalAbi.INTERNAL_ABI_ORIGIN
returnType = declaration.defaultType
}
context.createIrBuilder(function.symbol).apply {
function.body = irBlockBody {
+irReturn(irGetObjectValue(declaration.defaultType, declaration.symbol))
}
}
context.internalAbi.declare(function, declaration.module)
}
}
}
module.acceptChildrenVoid(visitor)
}
)
internal val useInternalAbiPhase = makeKonanModuleOpPhase(
name = "useInternalAbi",
description = "Use internal ABI functions to access private entities",
prerequisite = emptySet(),
op = { context, module ->
val accessors = mutableMapOf<IrClass, IrSimpleFunction>()
val transformer = object : IrElementTransformerVoid() {
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
val irClass = expression.symbol.owner
if (!irClass.isCompanion || context.llvmModuleSpecification.containsDeclaration(irClass)) {
return expression
}
val parent = irClass.parentAsClass
if (parent.isObjCClass()) {
// Access to Obj-C metaclass is done via intrinsic.
return expression
}
val accessor = accessors.getOrPut(irClass) {
context.irFactory.buildFun {
name = InternalAbi.getCompanionObjectAccessorName(irClass)
returnType = irClass.defaultType
origin = InternalAbi.INTERNAL_ABI_ORIGIN
isExternal = true
}.also {
context.internalAbi.reference(it, irClass.module)
}
}
return IrCallImpl(expression.startOffset, expression.endOffset, expression.type, accessor.symbol, accessor.typeParameters.size, accessor.valueParameters.size)
}
}
module.transformChildrenVoid(transformer)
}
)
internal val bitcodePhase = NamedCompilerPhase(
name = "Bitcode",
description = "LLVM Bitcode generation",
lower = contextLLVMSetupPhase then
buildDFGPhase then
devirtualizationPhase then
redundantCoercionsCleaningPhase then
dcePhase then
createLLVMDeclarationsPhase then
ghaPhase then
RTTIPhase then
generateDebugInfoHeaderPhase then
propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields
// from dependencies can be changed during lowerings.
escapeAnalysisPhase then
localEscapeAnalysisPhase then
codegenPhase then
finalizeDebugInfoPhase then
cStubsPhase
)
private val backendCodegen = namedUnitPhase(
name = "Backend codegen",
description = "Backend code generation",
lower = takeFromContext<Context, Unit, IrModuleFragment> { it.irModule!! } then
allLoweringsPhase then // Lower current module first.
dependenciesLowerPhase then // Then lower all libraries in topological order.
// With that we guarantee that inline functions are unlowered while being inlined.
entryPointPhase then
exportInternalAbiPhase then
useInternalAbiPhase then
bitcodePhase then
verifyBitcodePhase then
printBitcodePhase then
linkBitcodeDependenciesPhase then
bitcodeOptimizationPhase then
unitSink()
)
// Have to hide Context as type parameter in order to expose toplevelPhase outside of this module.
val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase(
name = "Compiler",
description = "The whole compilation process",
lower = frontendPhase then
createSymbolTablePhase then
objCExportPhase then
buildCExportsPhase then
psiToIrPhase then
destroySymbolTablePhase then
copyDefaultValuesToActualPhase then
serializerPhase then
specialBackendChecksPhase then
namedUnitPhase(
name = "Backend",
description = "All backend",
lower = backendCodegen then
produceOutputPhase then
disposeLLVMPhase then
unitSink()
) then
objectFilesPhase then
linkerPhase then
freeNativeMemPhase
)
internal fun PhaseConfig.disableIf(phase: AnyNamedPhase, condition: Boolean) {
if (condition) disable(phase)
}
internal fun PhaseConfig.disableUnless(phase: AnyNamedPhase, condition: Boolean) {
if (!condition) disable(phase)
}
internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
with(config.configuration) {
disable(compileTimeEvaluatePhase)
disable(localEscapeAnalysisPhase)
// Don't serialize anything to a final executable.
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
disableUnless(exportInternalAbiPhase, config.produce.isCache)
disableIf(backendCodegen, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage)
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
disableUnless(linkerPhase, config.produce.involvesLinkStage)
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(escapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken
// debug information.
disableUnless(propertyAccessorInlinePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
val isDescriptorsOnlyLibrary = config.metadataKlib == true
disableIf(psiToIrPhase, isDescriptorsOnlyLibrary)
disableIf(destroySymbolTablePhase, isDescriptorsOnlyLibrary)
disableIf(copyDefaultValuesToActualPhase, isDescriptorsOnlyLibrary)
disableIf(specialBackendChecksPhase, isDescriptorsOnlyLibrary)
}
}
@@ -0,0 +1,251 @@
package org.jetbrains.kotlin.backend.konan.cgen
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.irBuilder
import org.jetbrains.kotlin.ir.util.irCatch
import org.jetbrains.kotlin.konan.ForeignExceptionMode
import org.jetbrains.kotlin.name.Name
internal class CFunctionBuilder {
private val parameters = mutableListOf<CVariable>()
private lateinit var returnType: CType
var variadic: Boolean = false
fun setReturnType(type: CType) {
require(!::returnType.isInitialized)
returnType = type
}
fun addParameter(type: CType): CVariable {
val result = CVariable(type, "p${counter++}")
parameters += result
return result
}
val numberOfParameters: Int get() = parameters.size
private var counter = 1
fun getType(): CType = CTypes.function(returnType, parameters.map { it.type }, variadic)
fun buildSignature(name: String): String = returnType.render(buildString {
append(name)
append('(')
parameters.joinTo(this)
if (parameters.isEmpty()) {
if (!variadic) append("void")
} else {
if (variadic) append(", ...")
}
append(')')
})
}
internal class KotlinBridgeBuilder(
startOffset: Int,
endOffset: Int,
cName: String,
stubs: KotlinStubs,
isExternal: Boolean,
foreignExceptionMode: ForeignExceptionMode.Mode
) {
private var counter = 0
private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs, isExternal, foreignExceptionMode)
val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset)
fun addParameter(type: IrType): IrValueParameter {
val index = counter++
return IrValueParameterImpl(
bridge.startOffset, bridge.endOffset, bridge.origin,
IrValueParameterSymbolImpl(),
Name.identifier("p$index"), index, type,
null,
isCrossinline = false,
isNoinline = false,
isHidden = false,
isAssignable = false
).apply {
parent = bridge
bridge.valueParameters += this
}
}
fun setReturnType(type: IrType) {
bridge.returnType = type
}
fun build(): IrFunction = bridge
}
private fun createKotlinBridge(
startOffset: Int,
endOffset: Int,
cBridgeName: String,
stubs: KotlinStubs,
isExternal: Boolean,
foreignExceptionMode: ForeignExceptionMode.Mode
): IrFunction {
val bridge = IrFunctionImpl(
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
IrSimpleFunctionSymbolImpl(),
Name.identifier(cBridgeName),
DescriptorVisibilities.PRIVATE,
Modality.FINAL,
IrUninitializedType,
isInline = false,
isExternal = isExternal,
isTailrec = false,
isSuspend = false,
isExpect = false,
isFakeOverride = false,
isOperator = false,
isInfix = false
)
if (isExternal) {
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
stubs.symbols.symbolName.owner, cBridgeName)
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
stubs.symbols.filterExceptions.owner,
foreignExceptionMode.value)
} else {
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
stubs.symbols.exportForCppRuntime.owner, cBridgeName)
}
return bridge
}
internal class KotlinCBridgeBuilder(
startOffset: Int,
endOffset: Int,
cName: String,
stubs: KotlinStubs,
isKotlinToC: Boolean,
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default
) {
private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC, foreignExceptionMode)
private val cBridgeBuilder = CFunctionBuilder()
val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder
fun addParameter(kotlinType: IrType, cType: CType): Pair<IrValueParameter, CVariable> {
return kotlinBridgeBuilder.addParameter(kotlinType) to cBridgeBuilder.addParameter(cType)
}
fun setReturnType(kotlinReturnType: IrType, cReturnType: CType) {
kotlinBridgeBuilder.setReturnType(kotlinReturnType)
cBridgeBuilder.setReturnType(cReturnType)
}
fun buildCSignature(name: String): String = cBridgeBuilder.buildSignature(name)
fun buildKotlinBridge() = kotlinBridgeBuilder.build()
}
internal class KotlinCallBuilder(private val irBuilder: IrBuilderWithScope, private val symbols: KonanSymbols) {
val prepare = mutableListOf<IrStatement>()
val arguments = mutableListOf<IrExpression>()
val cleanup = mutableListOf<IrBuilderWithScope.() -> IrStatement>()
private var memScope: IrVariable? = null
fun getMemScope(): IrExpression = with(irBuilder) {
memScope?.let { return irGet(it) }
val newMemScope = scope.createTemporaryVariable(irCall(symbols.interopMemScope.owner.constructors.single()))
memScope = newMemScope
prepare += newMemScope
val clearImpl = symbols.interopMemScope.owner.simpleFunctions().single { it.name.asString() == "clearImpl" }
cleanup += {
irCall(clearImpl).apply {
dispatchReceiver = irGet(memScope!!)
}
}
irGet(newMemScope)
}
fun build(
function: IrFunction,
transformCall: (IrMemberAccessExpression<*>) -> IrExpression = { it }
): IrExpression {
val arguments = this.arguments.toMutableList()
val kotlinCall = irBuilder.irCall(function).run {
if (function.dispatchReceiverParameter != null) {
dispatchReceiver = arguments.removeAt(0)
}
if (function.extensionReceiverParameter != null) {
extensionReceiver = arguments.removeAt(0)
}
assert(arguments.size == function.valueParameters.size)
arguments.forEachIndexed { index, it -> putValueArgument(index, it) }
transformCall(this)
}
return if (prepare.isEmpty() && cleanup.isEmpty()) {
kotlinCall
} else {
irBuilder.irBlock(kotlinCall) {
prepare.forEach { +it }
if (cleanup.isEmpty()) {
+kotlinCall
} else {
// Note: generating try-catch as finally blocks are already lowered.
val result = irTemporary(IrTryImpl(startOffset, endOffset, kotlinCall.type).apply {
tryResult = kotlinCall
catches += irCatch(context.irBuiltIns.throwableType).apply {
result = irBlock(kotlinCall) {
cleanup.forEach { +it() }
+irThrow(irGet(catchParameter))
}
}
})
// TODO: consider handling a cleanup failure properly.
cleanup.forEach { +it() }
+irGet(result)
}
}
}
}
}
internal class CCallBuilder {
val arguments = mutableListOf<String>()
fun build(function: String) = buildString {
append(function)
append('(')
arguments.joinTo(this)
append(')')
}
}
@@ -0,0 +1,66 @@
package org.jetbrains.kotlin.backend.konan.cgen
internal interface CType {
fun render(name: String): String
}
internal class CVariable(val type: CType, val name: String) {
override fun toString() = type.render(name)
}
internal object CTypes {
fun simple(type: String): CType = SimpleCType(type)
fun pointer(pointee: CType): CType = PointerCType(pointee)
fun function(returnType: CType, parameterTypes: List<CType>, variadic: Boolean): CType =
FunctionCType(returnType, parameterTypes, variadic)
fun blockPointer(pointee: CType): CType = object : CType {
override fun render(name: String): String = pointee.render("^$name")
}
val void = simple("void")
val voidPtr = pointer(void)
val signedChar = simple("signed char")
val unsignedChar = simple("unsigned char")
val short = simple("short")
val unsignedShort = simple("unsigned short")
val int = simple("int")
val unsignedInt = simple("unsigned int")
val longLong = simple("long long")
val unsignedLongLong = simple("unsigned long long")
val float = simple("float")
val double = simple("double")
val C99Bool = simple("_Bool")
val char = simple("char")
val vector128 = simple("float __attribute__ ((__vector_size__ (16)))")
val id = simple("id")
}
private class SimpleCType(private val type: String) : CType {
override fun render(name: String): String = if (name.isEmpty()) type else "$type $name"
}
private class PointerCType(private val pointee: CType) : CType {
override fun render(name: String): String = pointee.render("*$name")
}
private class FunctionCType(
private val returnType: CType,
private val parameterTypes: List<CType>,
private val variadic: Boolean
) : CType {
override fun render(name: String): String = returnType.render(buildString {
append("(")
append(name)
append(")(")
parameterTypes.joinTo(this) { it.render("") }
if (parameterTypes.isEmpty()) {
if (!variadic) append("void")
} else {
if (variadic) append(", ...")
}
append(')')
})
}
@@ -0,0 +1,118 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.cgen
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
import org.jetbrains.kotlin.backend.konan.KonanFqNames
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.isObjCObjectType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal fun IrType.isCEnumType(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
if (simpleType.hasQuestionMark) return false
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
if (!enumClass.isEnumClass) return false
return enumClass.superTypes
.any { (it.classifierOrNull?.owner as? IrClass)?.fqNameForIrSerialization == FqName("kotlinx.cinterop.CEnum") }
}
private val cCall = RuntimeNames.cCall
// Make sure external stubs always get proper annotaions.
private fun IrDeclaration.hasCCallAnnotation(name: String): Boolean =
this.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
// LazyIr doesn't pass annotations from descriptor to IrValueParameter.
|| this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
internal fun IrValueParameter.isWCStringParameter() = hasCCallAnnotation("WCString")
internal fun IrValueParameter.isCStringParameter() = hasCCallAnnotation("CString")
internal fun IrValueParameter.isObjCConsumed() = hasCCallAnnotation("Consumed")
internal fun IrSimpleFunction.objCConsumesReceiver() = hasCCallAnnotation("ConsumesReceiver")
internal fun IrSimpleFunction.objCReturnsRetained() = hasCCallAnnotation("ReturnsRetained")
internal fun IrClass.getCStructSpelling(): String? =
getAnnotationArgumentValue(FqName("kotlinx.cinterop.internal.CStruct"), "spelling")
internal fun IrType.isTypeOfNullLiteral(): Boolean = this is IrSimpleType && hasQuestionMark
&& classifier.isClassWithFqName(StandardNames.FqNames.nothing)
internal fun IrType.isVector(): Boolean {
if (this is IrSimpleType && !this.hasQuestionMark) {
return classifier.isClassWithFqName(KonanFqNames.Vector128.toUnsafe())
}
return false
}
internal fun IrType.isObjCReferenceType(target: KonanTarget, irBuiltIns: IrBuiltIns): Boolean {
if (!target.family.isAppleFamily) return false
// Handle the same types as produced by [objCPointerMirror] in Interop/StubGenerator/.../Mappings.kt.
if (isObjCObjectType()) return true
val descriptor = classifierOrNull?.descriptor ?: return false
val builtIns = irBuiltIns.builtIns
return when (descriptor) {
builtIns.any,
builtIns.string,
builtIns.list, builtIns.mutableList,
builtIns.set,
builtIns.map -> true
else -> false
}
}
internal fun IrType.isCPointer(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCPointer
internal fun IrType.isCValue(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCValue
internal fun IrType.isNativePointed(symbols: KonanSymbols): Boolean = isSubtypeOfClass(symbols.nativePointed)
internal fun IrType.isCStructFieldTypeStoredInMemoryDirectly(): Boolean = isPrimitiveType() || isUnsigned() || isVector()
internal fun IrType.isCStructFieldSupportedReferenceType(symbols: KonanSymbols): Boolean =
isObjCObjectType()
|| getClass()?.isAny() == true
|| isStringClassType()
|| classOrNull == symbols.list
|| classOrNull == symbols.mutableList
|| classOrNull == symbols.set
|| classOrNull == symbols.map
/**
* Check given function is a getter or setter
* for `value` property of CEnumVar subclass.
*/
internal fun IrFunction.isCEnumVarValueAccessor(symbols: KonanSymbols): Boolean {
val parent = parent as? IrClass ?: return false
return if (symbols.interopCEnumVar in parent.superClasses && isPropertyAccessor) {
(propertyIfAccessor as IrProperty).name.asString() == "value"
} else {
false
}
}
internal fun IrFunction.isCStructMemberAtAccessor() = hasAnnotation(RuntimeNames.cStructMemberAt)
internal fun IrFunction.isCStructArrayMemberAtAccessor() = hasAnnotation(RuntimeNames.cStructArrayMemberAt)
internal fun IrFunction.isCStructBitFieldAccessor() = hasAnnotation(RuntimeNames.cStructBitField)
@@ -0,0 +1,466 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import llvm.LLVMStoreSizeOfType
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.llvm.computeFunctionName
import org.jetbrains.kotlin.backend.konan.llvm.llvmType
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.backend.konan.lower.InnerClassLowering
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.FqName
internal class OverriddenFunctionInfo(
val function: IrSimpleFunction,
val overriddenFunction: IrSimpleFunction
) {
val needBridge: Boolean
get() = function.target.needBridgeTo(overriddenFunction)
val bridgeDirections: BridgeDirections
get() = function.target.bridgeDirectionsTo(overriddenFunction)
val canBeCalledVirtually: Boolean
get() {
if (overriddenFunction.isObjCClassMethod()) {
return function.canObjCClassMethodBeCalledVirtually(overriddenFunction)
}
return overriddenFunction.isOverridable
}
val inheritsBridge: Boolean
get() = !function.isReal
&& function.target.overrides(overriddenFunction)
&& function.bridgeDirectionsTo(overriddenFunction).allNotNeeded()
fun getImplementation(context: Context): IrSimpleFunction? {
val target = function.target
val implementation = if (!needBridge)
target
else {
val bridgeOwner = if (inheritsBridge) {
target // Bridge is inherited from superclass.
} else {
function
}
context.specialDeclarationsFactory.getBridge(OverriddenFunctionInfo(bridgeOwner, overriddenFunction))
}
return if (implementation.modality == Modality.ABSTRACT) null else implementation
}
override fun toString(): String {
return "(descriptor=$function, overriddenDescriptor=$overriddenFunction)"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is OverriddenFunctionInfo) return false
if (function != other.function) return false
if (overriddenFunction != other.overriddenFunction) return false
return true
}
override fun hashCode(): Int {
var result = function.hashCode()
result = 31 * result + overriddenFunction.hashCode()
return result
}
}
internal class ClassGlobalHierarchyInfo(val classIdLo: Int, val classIdHi: Int,
val interfaceId: Int, val interfaceColor: Int) {
companion object {
val DUMMY = ClassGlobalHierarchyInfo(0, 0, 0, 0)
// 32-items table seems like a good threshold.
val MAX_BITS_PER_COLOR = 5
}
}
internal class GlobalHierarchyAnalysisResult(val bitsPerColor: Int)
internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrModuleFragment) {
fun run() {
/*
* The algorithm for fast interface call and check:
* Consider the following graph: the vertices are interfaces and two interfaces are
* connected with an edge if there exists a class which inherits both of them.
* Now find a proper vertex-coloring of that graph (such that no edge connects vertices of same color).
* Assign to each interface a unique id in such a way that its color is stored in the lower bits of its id.
* Assuming the number of colors used is reasonably small build then a perfect hash table for each class:
* for each interfaceId inherited: itable[interfaceId % size] == interfaceId
* Since we store the color in the lower bits the division can be replaced with (interfaceId & (size - 1)).
* This is indeed a perfect hash table by construction of the coloring of the interface graph.
* Now to perform an interface call store in all itables pointers to vtables of that particular interface.
* Interface call: *(itable[interfaceId & (size - 1)].vtable[methodIndex])(...)
* Interface check: itable[interfaceId & (size - 1)].id == interfaceId
*
* Note that we have a fallback to a more conservative version if the size of an itable is too large:
* just save all interface ids and vtables in sorted order and find the needed one with the binary search.
* We can signal that using the sign bit of the type info's size field:
* if (size >= 0) { .. fast path .. }
* else binary_search(0, -size)
*/
val interfaceColors = assignColorsToInterfaces()
val maxColor = interfaceColors.values.maxOrNull() ?: 0
var bitsPerColor = 0
var x = maxColor
while (x > 0) {
++bitsPerColor
x /= 2
}
val maxInterfaceId = Int.MAX_VALUE shr bitsPerColor
val colorCounts = IntArray(maxColor + 1)
/*
* Here's the explanation of what's happening here:
* Given a tree we can traverse it with the DFS and save for each vertex two times:
* the enter time (the first time we saw this vertex) and the exit time (the last time we saw it).
* It turns out that if we assign then for each vertex the interval (enterTime, exitTime),
* then the following claim holds for any two vertices v and w:
* ----- v is ancestor of w iff interval(v) contains interval(w) ------
* Now apply this idea to the classes hierarchy tree and we'll get a fast type check.
*
* And one more observation: for each pair of intervals they either don't intersect or
* one contains the other. With that in mind, we can save in a type info only one end of an interval.
*/
val root = context.irBuiltIns.anyClass.owner
val immediateInheritors = mutableMapOf<IrClass, MutableList<IrClass>>()
val allClasses = mutableListOf<IrClass>()
irModule.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
if (declaration.isInterface) {
val color = interfaceColors[declaration]!!
// Numerate from 1 (reserve 0 for invalid value).
val interfaceId = ++colorCounts[color]
assert (interfaceId <= maxInterfaceId) {
"Unable to assign interface id to ${declaration.name}"
}
context.getLayoutBuilder(declaration).hierarchyInfo =
ClassGlobalHierarchyInfo(0, 0,
color or (interfaceId shl bitsPerColor), color)
} else {
allClasses += declaration
if (declaration != root) {
val superClass = declaration.getSuperClassNotAny() ?: root
val inheritors = immediateInheritors.getOrPut(superClass) { mutableListOf() }
inheritors.add(declaration)
}
}
super.visitClass(declaration)
}
})
var time = 0
fun dfs(irClass: IrClass) {
++time
// Make the Any's interval's left border -1 in order to correctly generate classes for ObjC blocks.
val enterTime = if (irClass == root) -1 else time
immediateInheritors[irClass]?.forEach { dfs(it) }
val exitTime = time
context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime, 0, 0)
}
dfs(root)
context.globalHierarchyAnalysisResult = GlobalHierarchyAnalysisResult(bitsPerColor)
}
class InterfacesForbiddennessGraph(val nodes: List<IrClass>, val forbidden: List<List<Int>>) {
fun computeColoringGreedy(): IntArray {
val colors = IntArray(nodes.size) { -1 }
var numberOfColors = 0
val usedColors = BooleanArray(nodes.size)
for (v in nodes.indices) {
for (c in 0 until numberOfColors)
usedColors[c] = false
for (u in forbidden[v])
if (colors[u] >= 0)
usedColors[colors[u]] = true
var found = false
for (c in 0 until numberOfColors)
if (!usedColors[c]) {
colors[v] = c
found = true
break
}
if (!found)
colors[v] = numberOfColors++
}
return colors
}
companion object {
fun build(irModuleFragment: IrModuleFragment): InterfacesForbiddennessGraph {
val interfaceIndices = mutableMapOf<IrClass, Int>()
val interfaces = mutableListOf<IrClass>()
val forbidden = mutableListOf<MutableList<Int>>()
irModuleFragment.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
fun registerInterface(iface: IrClass) {
interfaceIndices.getOrPut(iface) {
forbidden.add(mutableListOf())
interfaces.add(iface)
interfaces.size - 1
}
}
override fun visitClass(declaration: IrClass) {
if (declaration.isInterface)
registerInterface(declaration)
else {
val implementedInterfaces = declaration.implementedInterfaces
implementedInterfaces.forEach { registerInterface(it) }
for (i in 0 until implementedInterfaces.size)
for (j in i + 1 until implementedInterfaces.size) {
val v = interfaceIndices[implementedInterfaces[i]]!!
val u = interfaceIndices[implementedInterfaces[j]]!!
forbidden[v].add(u)
forbidden[u].add(v)
}
}
super.visitClass(declaration)
}
})
return InterfacesForbiddennessGraph(interfaces, forbidden)
}
}
}
private fun assignColorsToInterfaces(): Map<IrClass, Int> {
val graph = InterfacesForbiddennessGraph.build(irModule)
val coloring = graph.computeColoringGreedy()
return graph.nodes.mapIndexed { v, irClass -> irClass to coloring[v] }.toMap()
}
}
internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, val isLowered: Boolean) {
val vtableEntries: List<OverriddenFunctionInfo> by lazy {
assert(!irClass.isInterface)
context.logMultiple {
+""
+"BUILDING vTable for ${irClass.render()}"
}
val superVtableEntries = if (irClass.isSpecialClassWithNoSupertypes()) {
emptyList()
} else {
val superClass = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner
context.getLayoutBuilder(superClass).vtableEntries
}
val methods = irClass.sortedOverridableOrOverridingMethods
val newVtableSlots = mutableListOf<OverriddenFunctionInfo>()
val overridenVtableSlots = mutableMapOf<IrSimpleFunction, OverriddenFunctionInfo>()
context.logMultiple {
+""
+"SUPER vTable:"
superVtableEntries.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()}" }
+""
+"METHODS:"
methods.forEach { +" ${it.render()}" }
+""
+"BUILDING INHERITED vTable"
}
val superVtableMap = superVtableEntries.groupBy { it.function }
methods.forEach { overridingMethod ->
overridingMethod.allOverriddenFunctions.forEach {
val superMethods = superVtableMap[it]
if (superMethods?.isNotEmpty() == true) {
newVtableSlots.add(OverriddenFunctionInfo(overridingMethod, it))
superMethods.forEach { superMethod ->
overridenVtableSlots[superMethod.overriddenFunction] =
OverriddenFunctionInfo(overridingMethod, superMethod.overriddenFunction)
}
}
}
}
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
overridenVtableSlots[superMethod.overriddenFunction]?.also {
context.log { "Taking overridden ${superMethod.overriddenFunction.render()} -> ${it.function.render()}" }
} ?: superMethod.also {
context.log { "Taking super ${superMethod.overriddenFunction.render()} -> ${superMethod.function.render()}" }
}
}
// Add all possible (descriptor, overriddenDescriptor) edges for now, redundant will be removed later.
methods.mapTo(newVtableSlots) { OverriddenFunctionInfo(it, it) }
val inheritedVtableSlotsSet = inheritedVtableSlots.map { it.function to it.bridgeDirections }.toSet()
val filteredNewVtableSlots = newVtableSlots
.filterNot { inheritedVtableSlotsSet.contains(it.function to it.bridgeDirections) }
.distinctBy { it.function to it.bridgeDirections }
.filter { it.function.isOverridable }
context.logMultiple {
+""
+"INHERITED vTable slots:"
inheritedVtableSlots.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()}" }
+""
+"MY OWN vTable slots:"
filteredNewVtableSlots.forEach { +" ${it.overriddenFunction.render()} -> ${it.function.render()} ${it.function}" }
+"DONE vTable for ${irClass.render()}"
}
inheritedVtableSlots + filteredNewVtableSlots.sortedBy { it.overriddenFunction.uniqueId }
}
fun vtableIndex(function: IrSimpleFunction): Int {
val bridgeDirections = function.target.bridgeDirectionsTo(function)
val index = vtableEntries.indexOfFirst { it.function == function && it.bridgeDirections == bridgeDirections }
if (index < 0) throw Error(function.render() + " $function " + " (${function.symbol.descriptor}) not in vtable of " + irClass.render())
return index
}
val methodTableEntries: List<OverriddenFunctionInfo> by lazy {
irClass.sortedOverridableOrOverridingMethods
.flatMap { method -> method.allOverriddenFunctions.map { OverriddenFunctionInfo(method, it) } }
.filter { it.canBeCalledVirtually }
.distinctBy { it.overriddenFunction.uniqueId }
.sortedBy { it.overriddenFunction.uniqueId }
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
val interfaceTableEntries: List<IrSimpleFunction> by lazy {
irClass.sortedOverridableOrOverridingMethods
.filter { f ->
f.isReal || f.overriddenSymbols.any { OverriddenFunctionInfo(f, it.owner).needBridge }
}
.toList()
}
data class InterfaceTablePlace(val interfaceId: Int, val itableSize: Int, val methodIndex: Int) {
companion object {
val INVALID = InterfaceTablePlace(0, -1, -1)
}
}
fun itablePlace(function: IrSimpleFunction): InterfaceTablePlace {
assert (irClass.isInterface) { "An interface expected but was ${irClass.name}" }
val itable = interfaceTableEntries
val index = itable.indexOf(function)
if (index >= 0)
return InterfaceTablePlace(hierarchyInfo.interfaceId, itable.size, index)
val superFunction = function.overriddenSymbols.first().owner
return context.getLayoutBuilder(superFunction.parentAsClass).itablePlace(superFunction)
}
/**
* All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/
val fields: List<IrField> by lazy {
val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fields else emptyList()
superFields + getDeclaredFields()
}
val associatedObjects by lazy {
val result = mutableMapOf<IrClass, IrClass>()
irClass.annotations.forEach {
val irFile = irClass.getContainingFile()
val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass
?: error(irFile, it, "unexpected annotation")
if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) {
val argument = it.getValueArgument(0)
val irClassReference = argument as? IrClassReference
?: error(irFile, argument, "unexpected annotation argument")
val associatedObject = irClassReference.symbol.owner
if (associatedObject !is IrClass || !associatedObject.isObject) {
error(irFile, irClassReference, "argument is not a singleton")
}
if (annotationClass in result) {
error(
irFile,
it,
"duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}"
)
}
result[annotationClass] = associatedObject
}
}
result
}
lateinit var hierarchyInfo: ClassGlobalHierarchyInfo
/**
* Fields declared in the class.
*/
private fun getDeclaredFields(): List<IrField> {
val declarations: List<IrDeclaration> = if (irClass.isInner && !isLowered) {
// Note: copying to avoid mutation of the original class.
irClass.declarations.toMutableList()
.also { InnerClassLowering.addOuterThisField(it, irClass, context) }
} else {
irClass.declarations
}
val fields = declarations.mapNotNull {
when (it) {
is IrField -> it.takeIf { it.isReal }
is IrProperty -> it.takeIf { it.isReal }?.backingField
else -> null
}
}
if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields"))))
return fields
return fields.sortedByDescending{ LLVMStoreSizeOfType(context.llvm.runtime.targetData, it.type.llvmType(context)) }
}
private val IrClass.sortedOverridableOrOverridingMethods: List<IrSimpleFunction>
get() =
this.simpleFunctions()
.filter { it.isOverridableOrOverrides && it.bridgeTarget == null }
.sortedBy { it.uniqueId }
private val functionIds = mutableMapOf<IrFunction, Long>()
private val IrFunction.uniqueId get() = functionIds.getOrPut(this) { computeFunctionName().localHash.value }
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.util.nTabs
import org.jetbrains.kotlin.descriptors.*
class DeepPrintVisitor(worker: DeclarationDescriptorVisitor<Boolean, Int>): DeepVisitor<Int>(worker) {
override fun visitChildren(descriptor: DeclarationDescriptor?, data: Int): Boolean {
return super.visitChildren(descriptor, data+1)
}
override fun visitChildren(descriptors: Collection<DeclarationDescriptor>, data: Int): Boolean {
return super.visitChildren(descriptors, data+1)
}
}
class PrintVisitor: DeclarationDescriptorVisitor<Boolean, Int> {
fun printDescriptor(descriptor: DeclarationDescriptor, amount: Int): Boolean {
println("${nTabs(amount)} ${descriptor.toString()}")
return true;
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitScriptDescriptor(descriptor: ScriptDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: Int): Boolean
= printDescriptor(descriptor, data)
}
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
open class DeepVisitor<D>(val worker: DeclarationDescriptorVisitor<Boolean, D>) : DeclarationDescriptorVisitor<Boolean, D> {
open fun visitChildren(descriptors: Collection<DeclarationDescriptor>, data: D): Boolean {
for (descriptor in descriptors) {
if (!descriptor.accept(this, data)) return false
}
return true
}
open fun visitChildren(descriptor: DeclarationDescriptor?, data: D): Boolean {
if (descriptor == null) return true
return descriptor.accept(this, data)
}
fun applyWorker(descriptor: DeclarationDescriptor, data: D): Boolean {
return descriptor.accept(worker, data)
}
fun processCallable(descriptor: CallableDescriptor, data: D): Boolean {
return applyWorker(descriptor, data)
&& visitChildren(descriptor.getTypeParameters(), data)
&& visitChildren(descriptor.getExtensionReceiverParameter(), data)
&& visitChildren(descriptor.getValueParameters(), data)
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data) && visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope()), data)
}
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data) && visitChildren(DescriptorUtils.getAllDescriptors(descriptor.memberScope), data)
}
override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: D): Boolean? {
return processCallable(descriptor, data)
}
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: D): Boolean? {
return processCallable(descriptor, data)
&& visitChildren(descriptor.getter, data)
&& visitChildren(descriptor.setter, data)
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: D): Boolean? {
return processCallable(descriptor, data)
}
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data)
}
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data)
&& visitChildren(descriptor.getThisAsReceiverParameter(), data)
&& visitChildren(descriptor.getConstructors(), data)
&& visitChildren(descriptor.getTypeConstructor().getParameters(), data)
&& visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().memberScope), data)
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data) && visitChildren(descriptor.getDeclaredTypeParameters(), data)
}
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data) && visitChildren(descriptor.getPackage(FqName.ROOT), data)
}
override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, data: D): Boolean? {
return visitFunctionDescriptor(constructorDescriptor, data)
}
override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: D): Boolean? {
return visitClassDescriptor(scriptDescriptor, data)
}
override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: D): Boolean? {
return visitVariableDescriptor(descriptor, data)
}
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: D): Boolean? {
return visitFunctionDescriptor(descriptor, data)
}
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: D): Boolean? {
return visitFunctionDescriptor(descriptor, data)
}
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: D): Boolean? {
return applyWorker(descriptor, data)
}
}
open public class EmptyDescriptorVisitorVoid: DeclarationDescriptorVisitor<Boolean, Unit> {
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Unit) = true
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: Unit) = true
override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: Unit) = true
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit) = true
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: Unit) = true
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Unit) = true
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Unit) = true
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Unit) = true
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit) = true
override fun visitScriptDescriptor(descriptor: ScriptDescriptor, data: Unit) = true
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit) = true
override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: Unit) = true
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: Unit) = true
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: Unit) = true
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: Unit) = true
}
@@ -0,0 +1,308 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.llvm.isVoidAsReturnType
import org.jetbrains.kotlin.backend.konan.llvm.longName
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
/**
* List of all implemented interfaces (including those which implemented by a super class)
*/
internal val IrClass.implementedInterfaces: List<IrClass>
get() {
val superClassImplementedInterfaces = this.getSuperClassNotAny()?.implementedInterfaces ?: emptyList()
val superInterfaces = this.getSuperInterfaces()
val superInterfacesImplementedInterfaces = superInterfaces.flatMap { it.implementedInterfaces }
return (superClassImplementedInterfaces +
superInterfacesImplementedInterfaces +
superInterfaces).distinct()
}
internal val IrFunction.isTypedIntrinsic: Boolean
get() = annotations.hasAnnotation(KonanFqNames.typedIntrinsic)
internal val arrayTypes = setOf(
"kotlin.Array",
"kotlin.ByteArray",
"kotlin.CharArray",
"kotlin.ShortArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray",
"kotlin.native.ImmutableBlob",
"kotlin.native.internal.NativePtrArray"
)
internal val arraysWithFixedSizeItems = setOf(
"kotlin.ByteArray",
"kotlin.CharArray",
"kotlin.ShortArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray"
)
internal val IrClass.isArray: Boolean
get() = this.fqNameForIrSerialization.asString() in arrayTypes
internal val IrClass.isArrayWithFixedSizeItems: Boolean
get() = this.fqNameForIrSerialization.asString() in arraysWithFixedSizeItems
fun IrClass.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
private enum class TypeKind {
ABSENT,
VOID,
VALUE_TYPE,
REFERENCE
}
private data class TypeWithKind(val irType: IrType?, val kind: TypeKind) {
companion object {
fun fromType(irType: IrType?) = when {
irType == null -> TypeWithKind(null, TypeKind.ABSENT)
irType.isInlinedNative() -> TypeWithKind(irType, TypeKind.VALUE_TYPE)
else -> TypeWithKind(irType, TypeKind.REFERENCE)
}
}
}
private fun IrFunction.typeWithKindAt(index: ParameterIndex) = when (index) {
ParameterIndex.RETURN_INDEX -> when {
isSuspend -> TypeWithKind(null, TypeKind.REFERENCE)
returnType.isVoidAsReturnType() -> TypeWithKind(returnType, TypeKind.VOID)
else -> TypeWithKind.fromType(returnType)
}
ParameterIndex.DISPATCH_RECEIVER_INDEX -> TypeWithKind.fromType(dispatchReceiverParameter?.type)
ParameterIndex.EXTENSION_RECEIVER_INDEX -> TypeWithKind.fromType(extensionReceiverParameter?.type)
else -> TypeWithKind.fromType(this.valueParameters[index.unmap()].type)
}
private fun IrFunction.needBridgeToAt(target: IrFunction, index: ParameterIndex)
= bridgeDirectionToAt(target, index).kind != BridgeDirectionKind.NONE
@Suppress("EXPERIMENTAL_FEATURE_WARNING")
private inline class ParameterIndex(val index: Int) {
companion object {
val RETURN_INDEX = ParameterIndex(0)
val DISPATCH_RECEIVER_INDEX = ParameterIndex(1)
val EXTENSION_RECEIVER_INDEX = ParameterIndex(2)
fun map(index: Int) = ParameterIndex(index + 3)
fun allParametersCount(irFunction: IrFunction) = irFunction.valueParameters.size + 3
inline fun forEachIndex(irFunction: IrFunction, block: (ParameterIndex) -> Unit) =
(0 until allParametersCount(irFunction)).forEach { block(ParameterIndex(it)) }
}
fun unmap() = index - 3
}
internal fun IrFunction.needBridgeTo(target: IrFunction): Boolean {
ParameterIndex.forEachIndex(this) {
if (needBridgeToAt(target, it)) return true
}
return false
}
internal enum class BridgeDirectionKind {
NONE,
BOX,
UNBOX
}
internal data class BridgeDirection(val irClass: IrClass?, val kind: BridgeDirectionKind) {
companion object {
val NONE = BridgeDirection(null, BridgeDirectionKind.NONE)
}
}
private fun IrFunction.bridgeDirectionToAt(overriddenFunction: IrFunction, index: ParameterIndex): BridgeDirection {
val kind = typeWithKindAt(index).kind
val (irClass, otherKind) = overriddenFunction.typeWithKindAt(index)
return if (otherKind == kind)
BridgeDirection.NONE
else when (kind) {
TypeKind.VOID, TypeKind.REFERENCE -> BridgeDirection(irClass?.erasure(), BridgeDirectionKind.UNBOX)
TypeKind.VALUE_TYPE -> BridgeDirection(
irClass?.erasure().takeIf { otherKind == TypeKind.VOID } /* Otherwise erase to [Any?] */,
BridgeDirectionKind.BOX)
TypeKind.ABSENT -> error("TypeKind.ABSENT should be on both sides")
}
}
private tailrec fun IrType.erasure(): IrClass =
when (val classifier = classifierOrFail) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> classifier.owner.superTypes.first().erasure()
else -> error(classifier)
}
internal class BridgeDirections(private val array: Array<BridgeDirection>) {
constructor(irFunction: IrSimpleFunction, overriddenFunction: IrSimpleFunction)
: this(Array<BridgeDirection>(ParameterIndex.allParametersCount(irFunction)) {
irFunction.bridgeDirectionToAt(overriddenFunction, ParameterIndex(it))
})
fun allNotNeeded(): Boolean = array.all { it.kind == BridgeDirectionKind.NONE }
private fun getDirectionAt(index: ParameterIndex) = array[index.index]
val returnDirection get() = getDirectionAt(ParameterIndex.RETURN_INDEX)
val dispatchReceiverDirection get() = getDirectionAt(ParameterIndex.DISPATCH_RECEIVER_INDEX)
val extensionReceiverDirection get() = getDirectionAt(ParameterIndex.EXTENSION_RECEIVER_INDEX)
fun parameterDirectionAt(index: Int) = getDirectionAt(ParameterIndex.map(index))
override fun toString(): String {
val result = StringBuilder()
array.forEach {
result.append(when (it.kind) {
BridgeDirectionKind.BOX -> 'B'
BridgeDirectionKind.UNBOX -> 'U'
BridgeDirectionKind.NONE -> 'N'
})
}
return result.toString()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is BridgeDirections) return false
return array.size == other.array.size
&& array.indices.all { array[it] == other.array[it] }
}
override fun hashCode(): Int {
var result = 0
array.forEach { result = result * 31 + it.hashCode() }
return result
}
companion object {
fun none(irFunction: IrSimpleFunction) = BridgeDirections(irFunction, irFunction)
}
}
val IrSimpleFunction.allOverriddenFunctions: Set<IrSimpleFunction>
get() {
val result = mutableSetOf<IrSimpleFunction>()
fun traverse(function: IrSimpleFunction) {
if (function in result) return
result += function
function.overriddenSymbols.forEach { traverse(it.owner) }
}
traverse(this)
return result
}
internal fun IrSimpleFunction.bridgeDirectionsTo(overriddenFunction: IrSimpleFunction): BridgeDirections {
val ourDirections = BridgeDirections(this, overriddenFunction)
val target = this.target
if (!this.isReal && modality != Modality.ABSTRACT
&& target.overrides(overriddenFunction)
&& ourDirections == target.bridgeDirectionsTo(overriddenFunction)) {
// Bridge is inherited from superclass.
return BridgeDirections.none(this)
}
return ourDirections
}
internal tailrec fun IrDeclaration.findPackage(): IrPackageFragment {
val parent = this.parent
return parent as? IrPackageFragment
?: (parent as IrDeclaration).findPackage()
}
fun IrFunctionSymbol.isComparisonFunction(map: Map<IrClassifierSymbol, IrSimpleFunctionSymbol>): Boolean =
this in map.values
val IrDeclaration.isPropertyAccessor get() =
this is IrSimpleFunction && this.correspondingPropertySymbol != null
val IrDeclaration.isPropertyField get() =
this is IrField && this.correspondingPropertySymbol != null
val IrDeclaration.isTopLevelDeclaration get() =
parent !is IrDeclaration && !this.isPropertyAccessor && !this.isPropertyField
fun IrDeclaration.findTopLevelDeclaration(): IrDeclaration = when {
this.isTopLevelDeclaration ->
this
this.isPropertyAccessor ->
(this as IrSimpleFunction).correspondingPropertySymbol!!.owner.findTopLevelDeclaration()
this.isPropertyField ->
(this as IrField).correspondingPropertySymbol!!.owner.findTopLevelDeclaration()
else ->
(this.parent as IrDeclaration).findTopLevelDeclaration()
}
internal val IrClass.isFrozen: Boolean
get() = annotations.hasAnnotation(KonanFqNames.frozen) ||
// RTTI is used for non-reference type box:
!this.defaultType.binaryTypeIsReference()
fun IrConstructorCall.getAnnotationStringValue() = getValueArgument(0).safeAs<IrConst<String>>()?.value
fun IrConstructorCall.getAnnotationStringValue(name: String): String {
val parameter = symbol.owner.valueParameters.single { it.name.asString() == name }
return getValueArgument(parameter.index).cast<IrConst<String>>().value
}
fun AnnotationDescriptor.getAnnotationStringValue(name: String): String {
return argumentValue(name)?.safeAs<StringValue>()?.value ?: error("Expected value $name at annotation $this")
}
fun <T> IrConstructorCall.getAnnotationValueOrNull(name: String): T? {
val parameter = symbol.owner.valueParameters.atMostOne { it.name.asString() == name }
return parameter?.let { getValueArgument(it.index)?.let { (it.cast<IrConst<T>>()).value } }
}
fun IrFunction.externalSymbolOrThrow(): String? {
annotations.findAnnotation(RuntimeNames.symbolNameAnnotation)?.let { return it.getAnnotationStringValue() }
if (annotations.hasAnnotation(KonanFqNames.objCMethod)) return null
if (annotations.hasAnnotation(KonanFqNames.typedIntrinsic)) return null
if (annotations.hasAnnotation(RuntimeNames.cCall)) return null
if (origin == InternalAbi.INTERNAL_ABI_ORIGIN) return null
throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation")
}
val IrFunction.isBuiltInOperator get() = origin == IrBuiltIns.BUILTIN_OPERATOR
fun IrDeclaration.isFromMetadataInteropLibrary() =
descriptor.module.isFromInteropLibrary()
@@ -0,0 +1,98 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrSetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.constructors
internal class KonanSharedVariablesManager(val context: KonanBackendContext) : SharedVariablesManager {
private val refClass = context.ir.symbols.refClass
private val refClassConstructor = refClass.constructors.single()
private val elementProperty = refClass.owner.declarations.filterIsInstance<IrProperty>().single()
override fun declareSharedVariable(originalDeclaration: IrVariable): IrVariable {
val valueType = originalDeclaration.type
val refConstructorCall = IrConstructorCallImpl.fromSymbolOwner(
originalDeclaration.startOffset, originalDeclaration.endOffset,
refClass.typeWith(valueType),
refClassConstructor
).apply {
putTypeArgument(0, valueType)
}
return with(originalDeclaration) {
IrVariableImpl(
startOffset, endOffset, origin,
IrVariableSymbolImpl(), name, refConstructorCall.type,
isVar = false,
isConst = false,
isLateinit = false
).apply {
initializer = refConstructorCall
}
}
}
override fun defineSharedValue(originalDeclaration: IrVariable, sharedVariableDeclaration: IrVariable): IrStatement {
val initializer = originalDeclaration.initializer ?: return sharedVariableDeclaration
val sharedVariableInitialization =
IrCallImpl(initializer.startOffset, initializer.endOffset,
context.irBuiltIns.unitType, elementProperty.setter!!.symbol,
elementProperty.setter!!.typeParameters.size, elementProperty.setter!!.valueParameters.size)
sharedVariableInitialization.dispatchReceiver =
IrGetValueImpl(initializer.startOffset, initializer.endOffset,
sharedVariableDeclaration.type, sharedVariableDeclaration.symbol)
sharedVariableInitialization.putValueArgument(0, initializer)
return IrCompositeImpl(
originalDeclaration.startOffset, originalDeclaration.endOffset, context.irBuiltIns.unitType, null,
listOf(sharedVariableDeclaration, sharedVariableInitialization)
)
}
override fun getSharedValue(sharedVariableSymbol: IrVariableSymbol, originalGet: IrGetValue) =
IrCallImpl(originalGet.startOffset, originalGet.endOffset,
originalGet.type, elementProperty.getter!!.symbol,
elementProperty.getter!!.typeParameters.size, elementProperty.getter!!.valueParameters.size).apply {
dispatchReceiver = IrGetValueImpl(
originalGet.startOffset, originalGet.endOffset,
sharedVariableSymbol.owner.type, sharedVariableSymbol
)
}
override fun setSharedValue(sharedVariableSymbol: IrVariableSymbol, originalSet: IrSetValue) =
IrCallImpl(originalSet.startOffset, originalSet.endOffset, context.irBuiltIns.unitType,
elementProperty.setter!!.symbol, elementProperty.setter!!.typeParameters.size,
elementProperty.setter!!.valueParameters.size).apply {
dispatchReceiver = IrGetValueImpl(
originalSet.startOffset, originalSet.endOffset,
sharedVariableSymbol.owner.type, sharedVariableSymbol
)
putValueArgument(0, originalSet.value)
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
/**
* Implementation of given method.
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(allowAbstract: Boolean = false): T {
if (this.kind.isReal) {
return this
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
// TODO: is it correct to take first?
@Suppress("UNCHECKED_CAST")
return filtered.first { allowAbstract || it.modality != Modality.ABSTRACT } as T
}
}
internal val ClassDescriptor.isArray: Boolean
get() = this.fqNameSafe.asString() in arrayTypes
internal val ClassDescriptor.isInterface: Boolean
get() = (this.kind == ClassKind.INTERFACE)
/**
* @return `konan.internal` member scope
*/
internal val KonanBuiltIns.kotlinNativeInternal: MemberScope
get() = this.builtInsModule.getPackage(RuntimeNames.kotlinNativeInternalPackageName).memberScope
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
internal fun ClassDescriptor.isNothing() = this.defaultType.isNothing()
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
get() {
val result = mutableListOf<T>()
fun traverse(descriptor: T) {
result.add(descriptor)
@Suppress("UNCHECKED_CAST")
descriptor.overriddenDescriptors.forEach { traverse(it as T) }
}
traverse(this)
return result
}
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
get () = unsubstitutedMemberScope.contributedMethods
internal val MemberScope.contributedMethods: List<FunctionDescriptor>
get () {
val contributedDescriptors = this.getContributedDescriptors()
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
val getters = properties.mapNotNull { it.getter }
val setters = properties.mapNotNull { it.setter }
return functions + getters + setters
}
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
internal val FunctionDescriptor.target: FunctionDescriptor
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
tailrec internal fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor {
return if (this is PackageFragmentDescriptor) this
else this.containingDeclaration!!.findPackage()
}
internal fun DeclarationDescriptor.findPackageView(): PackageViewDescriptor {
val packageFragment = this.findPackage()
return packageFragment.module.getPackage(packageFragment.fqName)
}
internal fun DeclarationDescriptor.allContainingDeclarations(): List<DeclarationDescriptor> {
var list = mutableListOf<DeclarationDescriptor>()
var current = this.containingDeclaration
while (current != null) {
list.add(current)
current = current.containingDeclaration
}
return list
}
fun AnnotationDescriptor.getStringValueOrNull(name: String): String? {
val constantValue = this.allValueArguments.entries.atMostOne {
it.key.asString() == name
}?.value
return constantValue?.value as String?
}
inline fun <reified T> AnnotationDescriptor.getArgumentValueOrNull(name: String): T? {
val constantValue = this.allValueArguments.entries.atMostOne {
it.key.asString() == name
}?.value
return constantValue?.value as T?
}
fun AnnotationDescriptor.getStringValue(name: String): String = this.getStringValueOrNull(name)!!
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
val result = mutableSetOf<FqName>()
val packageFragmentProvider = (module as? ModuleDescriptorImpl)?.packageFragmentProviderForModuleContentWithoutDependencies
fun getSubPackages(fqName: FqName) {
result.add(fqName)
val subPackages = packageFragmentProvider?.getSubPackagesOf(fqName) { true }
?: module.getSubPackagesOf(fqName) { true }
subPackages.forEach { getSubPackages(it) }
}
getSubPackages(FqName.ROOT)
return result
}
fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescriptor> =
getPackagesFqNames(this).flatMap {
getPackage(it).fragments.filter { it.module == this }.toSet()
}
val ClassDescriptor.enumEntries: List<ClassDescriptor>
get() {
assert(this.kind == ClassKind.ENUM_CLASS)
return this.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<ClassDescriptor>()
.filter { it.kind == ClassKind.ENUM_ENTRY }
}
internal val DeclarationDescriptor.isExpectMember: Boolean
get() = this is MemberDescriptor && this.isExpect
internal val DeclarationDescriptor.isSerializableExpectClass: Boolean
get() = this is ClassDescriptor && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(this)
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.klibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.kotlinLibrary
import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
import org.jetbrains.kotlin.library.BaseKotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
fun DeclarationDescriptor.deepPrint() {
this.accept(DeepPrintVisitor(PrintVisitor()), 0)
}
internal val String.synthesizedName get() = Name.identifier(this.synthesizedString)
internal val String.synthesizedString get() = "\$$this"
internal val DeclarationDescriptor.propertyIfAccessor
get() = if (this is PropertyAccessorDescriptor)
this.correspondingProperty
else this
internal val CallableMemberDescriptor.propertyIfAccessor
get() = if (this is PropertyAccessorDescriptor)
this.correspondingProperty
else this
internal val FunctionDescriptor.deserializedPropertyIfAccessor: DeserializedCallableMemberDescriptor
get() {
val member = this.propertyIfAccessor
if (member is DeserializedCallableMemberDescriptor)
return member
else
error("Unexpected deserializable callable descriptor")
}
internal val CallableMemberDescriptor.isDeserializableCallable
get () = (this.propertyIfAccessor is DeserializedCallableMemberDescriptor)
fun DeclarationDescriptor.findTopLevelDescriptor(): DeclarationDescriptor {
return if (this.containingDeclaration is org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor) this.propertyIfAccessor
else this.containingDeclaration!!.findTopLevelDescriptor()
}
val ModuleDescriptor.isForwardDeclarationModule: Boolean
get() {
// TODO: use KlibResolvedModuleDescriptorsFactoryImpl.FORWARD_DECLARATIONS_MODULE_NAME instead of
// manually created Name instance
return name == Name.special("<forward declarations>")
}
fun BaseKotlinLibrary.isInteropLibrary() =
manifestProperties["ir_provider"] == KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
fun ModuleDescriptor.isFromInteropLibrary() =
if (klibModuleOrigin !is DeserializedKlibModuleOrigin) false
else kotlinLibrary.isInteropLibrary()
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
fun createTopDownAnalyzerProviderForKonan(
moduleContext: ModuleContext,
bindingTrace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory,
languageVersionSettings: LanguageVersionSettings,
additionalPackages: List<PackageFragmentProvider>,
initContainer: StorageComponentContainer.() -> Unit
): ComponentProvider {
return createContainer("TopDownAnalyzerForKonan", NativePlatformAnalyzerServices) {
configureModule(moduleContext, NativePlatforms.unspecifiedNativePlatform, NativePlatformAnalyzerServices, bindingTrace, languageVersionSettings)
useInstance(declarationProviderFactory)
useImpl<AnnotationResolverImpl>()
CompilerEnvironment.configure(this)
useImpl<ResolveSession>()
useImpl<LazyTopDownAnalyzer>()
initContainer()
}.apply {
val packagePartProviders = mutableListOf(get<KotlinCodeAnalyzer>().packageFragmentProvider)
val moduleDescriptor = get<ModuleDescriptorImpl>()
packagePartProviders += additionalPackages
moduleDescriptor.initialize(CompositePackageFragmentProvider(packagePartProviders))
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.file
// This file contains some IR utilities which actually use descriptors.
// TODO: port this code to IR.
internal val IrDeclaration.llvmSymbolOrigin get() = when (this) {
is IrLazyDeclarationBase -> descriptor.llvmSymbolOrigin
else -> file.packageFragmentDescriptor.llvmSymbolOrigin
}
internal fun IrType.isObjCObjectType() = this.toKotlinType().isObjCObjectType()
@@ -0,0 +1,576 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal
import org.jetbrains.kotlin.backend.konan.llvm.findMainEntryPoint
import org.jetbrains.kotlin.backend.konan.lower.TestProcessor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.config.coroutinesIntrinsicsPackageFqName
import org.jetbrains.kotlin.config.coroutinesPackageFqName
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import kotlin.properties.Delegates
// This is what Context collects about IR.
internal class KonanIr(context: Context, irModule: IrModuleFragment): Ir<Context>(context, irModule) {
override var symbols: KonanSymbols by Delegates.notNull()
}
internal class KonanSymbols(
context: Context,
irBuiltIns: IrBuiltIns,
private val symbolTable: SymbolTable,
lazySymbolTable: ReferenceSymbolTable,
val functionIrClassFactory: BuiltInFictitiousFunctionIrClassFactory
): Symbols<Context>(context, irBuiltIns, symbolTable) {
val entryPoint = findMainEntryPoint(context)?.let { symbolTable.referenceSimpleFunction(it) }
override val externalSymbolTable = lazySymbolTable
val nothing = symbolTable.referenceClass(builtIns.nothing)
val throwable = symbolTable.referenceClass(builtIns.throwable)
val enum = symbolTable.referenceClass(builtIns.enum)
val nativePtr = symbolTable.referenceClass(context.nativePtr)
val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed)
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr)
val immutableBlobOf = symbolTable.referenceSimpleFunction(context.immutableBlobOf)
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
override val uByte = unsignedClass(UnsignedType.UBYTE)
override val uShort = unsignedClass(UnsignedType.USHORT)
override val uInt = unsignedClass(UnsignedType.UINT)
override val uLong = unsignedClass(UnsignedType.ULONG)
val signedIntegerClasses = setOf(byte, short, int, long)
val unsignedIntegerClasses = setOf(uByte, uShort, uInt, uLong)
val allIntegerClasses = signedIntegerClasses + unsignedIntegerClasses
val unsignedToSignedOfSameBitWidth = unsignedIntegerClasses.associate {
it to when (it) {
uByte -> byte
uShort -> short
uInt -> int
uLong -> long
else -> error(it.descriptor)
}
}
val integerConversions = allIntegerClasses.flatMap { fromClass ->
allIntegerClasses.map { toClass ->
val name = Name.identifier("to${toClass.descriptor.name.asString().capitalize()}")
val descriptor = if (fromClass in signedIntegerClasses && toClass in unsignedIntegerClasses) {
builtInsPackage("kotlin")
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
.single {
it.dispatchReceiverParameter == null &&
it.extensionReceiverParameter?.type == fromClass.descriptor.defaultType &&
it.valueParameters.isEmpty()
}
} else {
fromClass.descriptor.unsubstitutedMemberScope
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
.single {
it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
}
}
val symbol = symbolTable.referenceSimpleFunction(descriptor)
(fromClass to toClass) to symbol
}
}.toMap()
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
val symbolName = topLevelClass(RuntimeNames.symbolNameAnnotation)
val filterExceptions = topLevelClass(RuntimeNames.filterExceptions)
val exportForCppRuntime = topLevelClass(RuntimeNames.exportForCppRuntime)
val objCMethodImp = symbolTable.referenceClass(context.interopBuiltIns.objCMethodImp)
val onUnhandledException = internalFunction("OnUnhandledException")
val interopNativePointedGetRawPointer =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.nativePointedGetRawPointer)
val interopCPointer = symbolTable.referenceClass(context.interopBuiltIns.cPointer)
val interopCstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cstr.getter!!)
val interopWcstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.wcstr.getter!!)
val interopMemScope = symbolTable.referenceClass(context.interopBuiltIns.memScope)
val interopCValue = symbolTable.referenceClass(context.interopBuiltIns.cValue)
val interopCValues = symbolTable.referenceClass(context.interopBuiltIns.cValues)
val interopCValuesRef = symbolTable.referenceClass(context.interopBuiltIns.cValuesRef)
val interopCValueWrite = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueWrite)
val interopCValueRead = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueRead)
val interopAllocType = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocType)
val interopTypeOf = symbolTable.referenceSimpleFunction(context.interopBuiltIns.typeOf)
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
val interopForeignObjCObject = interopClass("ForeignObjCObject")
// These are possible supertypes of forward declarations - we need to reference them explicitly to force their deserialization.
// TODO: Do it lazily.
val interopCOpaque = symbolTable.referenceClass(context.interopBuiltIns.cOpaque)
val interopObjCObject = symbolTable.referenceClass(context.interopBuiltIns.objCObject)
val interopObjCObjectBase = symbolTable.referenceClass(context.interopBuiltIns.objCObjectBase)
val interopObjCRelease = interopFunction("objc_release")
val interopObjCRetain = interopFunction("objc_retain")
val interopObjcRetainAutoreleaseReturnValue = interopFunction("objc_retainAutoreleaseReturnValue")
val interopCreateObjCObjectHolder = interopFunction("createObjCObjectHolder")
val interopCreateKotlinObjectHolder = interopFunction("createKotlinObjectHolder")
val interopUnwrapKotlinObjectHolderImpl = interopFunction("unwrapKotlinObjectHolderImpl")
val interopCreateObjCSuperStruct = interopFunction("createObjCSuperStruct")
val interopGetMessenger = interopFunction("getMessenger")
val interopGetMessengerStret = interopFunction("getMessengerStret")
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
val interopObjCObjectSuperInitCheck =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectSuperInitCheck)
val interopObjCObjectInitBy = symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitBy)
val interopObjCObjectRawValueGetter =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr)
val interopNativePointedRawPtrGetter =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.nativePointedRawPtrGetter)
val interopCPointerRawValue =
symbolTable.referenceProperty(context.interopBuiltIns.cPointerRawValue)
val interopInterpretObjCPointer =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointer)
val interopInterpretObjCPointerOrNull =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointerOrNull)
val interopInterpretNullablePointed =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretNullablePointed)
val interopInterpretCPointer =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretCPointer)
val interopCreateNSStringFromKString =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString)
val createForeignException = interopFunction("CreateForeignException")
val interopObjCGetSelector = interopFunction("objCGetSelector")
val interopCEnumVar = interopClass("CEnumVar")
val nativeMemUtils = symbolTable.referenceClass(context.interopBuiltIns.nativeMemUtils)
val readBits = interopFunction("readBits")
val writeBits = interopFunction("writeBits")
val objCExportTrapOnUndeclaredException =
symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions(
Name.identifier("trapOnUndeclaredException"),
NoLookupLocation.FROM_BACKEND
).single())
val objCExportResumeContinuation = internalFunction("resumeContinuation")
val objCExportResumeContinuationWithException = internalFunction("resumeContinuationWithException")
val objCExportGetCoroutineSuspended = internalFunction("getCoroutineSuspended")
val objCExportInterceptedContinuation = internalFunction("interceptedContinuation")
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.getNativeNullPtr)
val boxCachePredicates = BoxCache.values().associate {
it to internalFunction("in${it.name.toLowerCase().capitalize()}BoxCache")
}
val boxCacheGetters = BoxCache.values().associate {
it to internalFunction("getCached${it.name.toLowerCase().capitalize()}Box")
}
val immutableBlob = symbolTable.referenceClass(
builtInsPackage("kotlin", "native").getContributedClassifier(
Name.identifier("ImmutableBlob"), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
val executeImpl = symbolTable.referenceSimpleFunction(
builtIns.builtInsModule.getPackage(FqName("kotlin.native.concurrent")).memberScope
.getContributedFunctions(Name.identifier("executeImpl"), NoLookupLocation.FROM_BACKEND)
.single()
)
val createCleaner = symbolTable.referenceSimpleFunction(
builtIns.builtInsModule.getPackage(FqName("kotlin.native.internal")).memberScope
.getContributedFunctions(Name.identifier("createCleaner"), NoLookupLocation.FROM_BACKEND)
.single()
)
val areEqualByValue = context.getKonanInternalFunctions("areEqualByValue").map {
symbolTable.referenceSimpleFunction(it)
}.associateBy { it.descriptor.valueParameters[0].type.computePrimitiveBinaryTypeOrNull()!! }
val reinterpret = internalFunction("reinterpret")
val ieee754Equals = context.getKonanInternalFunctions("ieee754Equals").map {
symbolTable.referenceSimpleFunction(it)
}
val equals = context.builtIns.any.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("equals"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) }
val throwArithmeticException = internalFunction("ThrowArithmeticException")
val throwIndexOutOfBoundsException = internalFunction("ThrowIndexOutOfBoundsException")
override val throwNullPointerException = internalFunction("ThrowNullPointerException")
override val throwNoWhenBranchMatchedException = internalFunction("ThrowNoWhenBranchMatchedException")
override val throwTypeCastException = internalFunction("ThrowTypeCastException")
override val throwKotlinNothingValueException = internalFunction("ThrowKotlinNothingValueException")
val throwClassCastException = internalFunction("ThrowClassCastException")
val throwInvalidReceiverTypeException = internalFunction("ThrowInvalidReceiverTypeException")
val throwIllegalStateException = internalFunction("ThrowIllegalStateException")
val throwIllegalStateExceptionWithMessage = internalFunction("ThrowIllegalStateExceptionWithMessage")
val throwIllegalArgumentException = internalFunction("ThrowIllegalArgumentException")
val throwIllegalArgumentExceptionWithMessage = internalFunction("ThrowIllegalArgumentExceptionWithMessage")
override val throwUninitializedPropertyAccessException = internalFunction("ThrowUninitializedPropertyAccessException")
override val stringBuilder = symbolTable.referenceClass(
builtInsPackage("kotlin", "text").getContributedClassifier(
Name.identifier("StringBuilder"), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
override val defaultConstructorMarker = symbolTable.referenceClass(
context.getKonanInternalClass("DefaultConstructorMarker")
)
val checkProgressionStep = context.getKonanInternalFunctions("checkProgressionStep")
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
val getProgressionLast = context.getKonanInternalFunctions("getProgressionLast")
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
val arrayContentToString = arrays.associateBy(
{ it },
{ findArrayExtension(it.descriptor, "contentToString") }
)
val arrayContentHashCode = arrays.associateBy(
{ it },
{ findArrayExtension(it.descriptor, "contentHashCode") }
)
private val kotlinCollectionsPackageScope: MemberScope
get() = builtInsPackage("kotlin", "collections")
private fun findArrayExtension(descriptor: ClassDescriptor, name: String): IrSimpleFunctionSymbol {
val functionDescriptor = kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
.singleOrNull {
it.valueParameters.isEmpty()
&& it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == descriptor
&& it.extensionReceiverParameter?.type?.isMarkedNullable == false
&& !it.isExpect
} ?: error(descriptor.toString())
return symbolTable.referenceSimpleFunction(functionDescriptor)
}
override val copyRangeTo get() = TODO()
fun getNoParamFunction(name: Name, receiverType: KotlinType): IrFunctionSymbol {
val descriptor = receiverType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
.first { it.valueParameters.isEmpty() }
return symbolTable.referenceFunction(descriptor)
}
val copyInto = arrays.map { symbol ->
val packageViewDescriptor = builtIns.builtInsModule.getPackage(StandardNames.COLLECTIONS_PACKAGE_FQ_NAME)
val functionDescriptor = packageViewDescriptor.memberScope
.getContributedFunctions(Name.identifier("copyInto"), NoLookupLocation.FROM_BACKEND)
.single {
!it.isExpect &&
it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == symbol.descriptor
}
symbol.descriptor to symbolTable.referenceSimpleFunction(functionDescriptor)
}.toMap()
val arrayGet = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) } }
val arraySet = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("set"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) } }
val arraySize = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("size"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it.getter!!) } }
val valuesForEnum = internalFunction("valuesForEnum")
val valueOfForEnum = internalFunction("valueOfForEnum")
val createUninitializedInstance = internalFunction("createUninitializedInstance")
val initInstance = internalFunction("initInstance")
val freeze = symbolTable.referenceSimpleFunction(
builtInsPackage("kotlin", "native", "concurrent").getContributedFunctions(
Name.identifier("freeze"), NoLookupLocation.FROM_BACKEND).single())
val println = symbolTable.referenceSimpleFunction(
builtInsPackage("kotlin", "io").getContributedFunctions(
Name.identifier("println"), NoLookupLocation.FROM_BACKEND)
.single { it.valueParameters.singleOrNull()?.type == builtIns.stringType })
val anyNToString = symbolTable.referenceSimpleFunction(
builtInsPackage("kotlin").getContributedFunctions(
Name.identifier("toString"), NoLookupLocation.FROM_BACKEND)
.single { it.extensionReceiverParameter?.type == builtIns.nullableAnyType})
override val getContinuation = internalFunction("getContinuation")
override val returnIfSuspended = internalFunction("returnIfSuspended")
val coroutineLaunchpad = internalFunction("coroutineLaunchpad")
override val suspendCoroutineUninterceptedOrReturn = internalFunction("suspendCoroutineUninterceptedOrReturn")
private val coroutinesIntrinsicsPackage = context.builtIns.builtInsModule.getPackage(
context.config.configuration.languageVersionSettings.coroutinesIntrinsicsPackageFqName()).memberScope
private val coroutinesPackage = context.builtIns.builtInsModule.getPackage(
context.config.configuration.languageVersionSettings.coroutinesPackageFqName()).memberScope
override val coroutineContextGetter = symbolTable.referenceSimpleFunction(
coroutinesPackage
.getContributedVariables(Name.identifier("coroutineContext"), NoLookupLocation.FROM_BACKEND)
.single()
.getter!!)
override val coroutineGetContext = internalFunction("getCoroutineContext")
override val coroutineImpl get() = TODO()
val baseContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.BaseContinuationImpl")
val restrictedContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.RestrictedContinuationImpl")
val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
val invokeSuspendFunction =
symbolTable.referenceSimpleFunction(
baseContinuationImpl.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("invokeSuspend"), NoLookupLocation.FROM_BACKEND)
.single()
)
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
coroutinesIntrinsicsPackage
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND)
.filterNot { it.isExpect }.single().getter!!
)
val cancellationException = topLevelClass(KonanFqNames.cancellationException)
val kotlinResult = topLevelClass("kotlin.Result")
val kotlinResultGetOrThrow = symbolTable.referenceSimpleFunction(
builtInsPackage("kotlin")
.getContributedFunctions(Name.identifier("getOrThrow"), NoLookupLocation.FROM_BACKEND)
.single {
it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == kotlinResult.descriptor
}
)
override val functionAdapter = symbolTable.referenceClass(context.getKonanInternalClass("FunctionAdapter"))
val refClass = symbolTable.referenceClass(context.getKonanInternalClass("Ref"))
val kFunctionImpl = symbolTable.referenceClass(context.reflectionTypes.kFunctionImpl)
val kSuspendFunctionImpl = symbolTable.referenceClass(context.reflectionTypes.kSuspendFunctionImpl)
val kMutableProperty0 = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty0)
val kMutableProperty1 = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty1)
val kMutableProperty2 = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty2)
val kProperty0Impl = symbolTable.referenceClass(context.reflectionTypes.kProperty0Impl)
val kProperty1Impl = symbolTable.referenceClass(context.reflectionTypes.kProperty1Impl)
val kProperty2Impl = symbolTable.referenceClass(context.reflectionTypes.kProperty2Impl)
val kMutableProperty0Impl = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty0Impl)
val kMutableProperty1Impl = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty1Impl)
val kMutableProperty2Impl = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty2Impl)
val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl)
val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl)
val typeOf = symbolTable.referenceSimpleFunction(context.reflectionTypes.typeOf)
val kType = symbolTable.referenceClass(context.reflectionTypes.kType)
val kVariance = symbolTable.referenceClass(context.reflectionTypes.kVariance)
val getClassTypeInfo = internalFunction("getClassTypeInfo")
val getObjectTypeInfo = internalFunction("getObjectTypeInfo")
val kClassImpl = internalClass("KClassImpl")
val kClassImplConstructor by lazy { kClassImpl.constructors.single() }
val kClassUnsupportedImpl = internalClass("KClassUnsupportedImpl")
val kClassUnsupportedImplConstructor by lazy { kClassUnsupportedImpl.constructors.single() }
val kTypeParameterImpl = internalClass("KTypeParameterImpl")
val kTypeImpl = internalClass("KTypeImpl")
val kTypeImplForTypeParametersWithRecursiveBounds = internalClass("KTypeImplForTypeParametersWithRecursiveBounds")
val kTypeProjection = symbolTable.referenceClass(context.reflectionTypes.kTypeProjection)
private val kTypeProjectionCompanionDescriptor = context.reflectionTypes.kTypeProjection.companionObjectDescriptor!!
val kTypeProjectionCompanion = symbolTable.referenceClass(kTypeProjectionCompanionDescriptor)
val kTypeProjectionStar = symbolTable.referenceProperty(
kTypeProjectionCompanionDescriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("STAR"), NoLookupLocation.FROM_BACKEND).single()
)
val kTypeProjectionFactories: Map<Variance, IrSimpleFunctionSymbol> = Variance.values().toList().associateWith {
val factoryName = when (it) {
Variance.INVARIANT -> "invariant"
Variance.IN_VARIANCE -> "contravariant"
Variance.OUT_VARIANCE -> "covariant"
}
symbolTable.referenceSimpleFunction(
kTypeProjectionCompanionDescriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier(factoryName), NoLookupLocation.FROM_BACKEND).single()
)
}
val emptyList = symbolTable.referenceSimpleFunction(
kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier("emptyList"), NoLookupLocation.FROM_BACKEND)
.single { it.valueParameters.isEmpty() }
)
val listOf = symbolTable.referenceSimpleFunction(
kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier("listOf"), NoLookupLocation.FROM_BACKEND)
.single { it.valueParameters.size == 1 && it.valueParameters[0].isVararg }
)
val listOfInternal = internalFunction("listOfInternal")
val threadLocal = symbolTable.referenceClass(
context.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.threadLocal))!!)
val sharedImmutable = symbolTable.referenceClass(
context.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.sharedImmutable))!!)
private fun topLevelClass(fqName: String): IrClassSymbol = topLevelClass(FqName(fqName))
private fun topLevelClass(fqName: FqName): IrClassSymbol = classById(ClassId.topLevel(fqName))
private fun classById(classId: ClassId): IrClassSymbol =
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(classId)!!)
private fun internalFunction(name: String): IrSimpleFunctionSymbol =
symbolTable.referenceSimpleFunction(context.getKonanInternalFunctions(name).single())
private fun internalClass(name: String): IrClassSymbol =
symbolTable.referenceClass(context.getKonanInternalClass(name))
private fun getKonanTestClass(className: String) = symbolTable.referenceClass(
builtInsPackage("kotlin", "native", "internal", "test").getContributedClassifier(
Name.identifier(className), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor)
private fun interopFunction(name: String) = symbolTable.referenceSimpleFunction(
context.interopBuiltIns.packageScope
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
.single()
)
private fun interopClass(name: String) = symbolTable.referenceClass(
context.interopBuiltIns.packageScope
.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
)
override fun functionN(n: Int) = functionIrClassFactory.functionN(n).symbol
override fun suspendFunctionN(n: Int) = functionIrClassFactory.suspendFunctionN(n).symbol
fun kFunctionN(n: Int) = functionIrClassFactory.kFunctionN(n).symbol
fun kSuspendFunctionN(n: Int) = functionIrClassFactory.kSuspendFunctionN(n).symbol
fun getKFunctionType(returnType: IrType, parameterTypes: List<IrType>) =
kFunctionN(parameterTypes.size).typeWith(parameterTypes + returnType)
val baseClassSuite = getKonanTestClass("BaseClassSuite")
val topLevelSuite = getKonanTestClass("TopLevelSuite")
val testFunctionKind = getKonanTestClass("TestFunctionKind")
private val testFunctionKindCache = TestProcessor.FunctionKind.values().associate {
val symbol = if (it.runtimeKindString.isEmpty())
null
else
symbolTable.referenceEnumEntry(testFunctionKind.descriptor.unsubstitutedMemberScope.getContributedClassifier(
Name.identifier(it.runtimeKindString), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor)
it to symbol
}
fun getTestFunctionKind(kind: TestProcessor.FunctionKind) = testFunctionKindCache[kind]!!
}
private fun getArrayListClassDescriptor(context: Context): ClassDescriptor {
val module = context.builtIns.builtInsModule
val pkg = module.getPackage(FqName.fromSegments(listOf("kotlin", "collections")))
val classifier = pkg.memberScope.getContributedClassifier(Name.identifier("ArrayList"),
NoLookupLocation.FROM_BACKEND)
return classifier as ClassDescriptor
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
val IrClassifierSymbol.typeWithoutArguments: IrType
get() = when (this) {
is IrClassSymbol -> {
require(this.descriptor.declaredTypeParameters.isEmpty())
this.typeWith(arguments = emptyList())
}
is IrTypeParameterSymbol -> this.defaultType
else -> error(this)
}
val IrClassifierSymbol.typeWithStarProjections
get() = when (this) {
is IrClassSymbol -> createType(
hasQuestionMark = false,
arguments = this.descriptor.declaredTypeParameters.map { IrStarProjectionImpl }
)
is IrTypeParameterSymbol -> this.defaultType
else -> error(this)
}
val IrTypeParameterSymbol.defaultType: IrType get() = IrSimpleTypeImpl(
this,
false,
emptyList(),
emptyList()
)
fun IrClass.typeWith(arguments: List<IrType>) = this.symbol.typeWith(arguments)
fun IrType.containsNull(): Boolean = if (this is IrSimpleType) {
if (this.hasQuestionMark) {
true
} else {
val classifier = this.classifier
when (classifier) {
is IrClassSymbol -> false
is IrTypeParameterSymbol -> classifier.owner.superTypes.any { it.containsNull() }
else -> error(classifier)
}
}
} else {
true
}
// TODO: get rid of these:
fun IrType.isSubtypeOf(other: KotlinType): Boolean = this.toKotlinType().isSubtypeOf(other)
fun IrType.isSubtypeOf(other: IrType): Boolean = this.isSubtypeOf(other.toKotlinType())
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
class ModuleIndex(val module: IrModuleFragment) {
/**
* Contains all classes declared in [module]
*/
val classes: Map<ClassDescriptor, IrClass>
/**
* Contains all functions declared in [module]
*/
val functions: Map<FunctionDescriptor, IrFunction>
init {
classes = mutableMapOf()
functions = mutableMapOf()
module.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
classes[declaration.descriptor] = declaration
}
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
functions[declaration.descriptor] = declaration
}
})
}
}
@@ -0,0 +1,132 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION
import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary
import org.jetbrains.kotlin.backend.konan.llvm.KonanMetadata
import org.jetbrains.kotlin.backend.konan.serialization.KonanFileMetadataSource
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleFragmentImpl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.klibModuleOrigin
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.isPublicApi
import org.jetbrains.kotlin.ir.types.IdSignatureValues
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private fun IrClass.isClassTypeWithSignature(signature: IdSignature.PublicSignature): Boolean {
if (!symbol.isPublicApi) return false
return signature == symbol.signature
}
fun IrClass.isUnit() = this.isClassTypeWithSignature(IdSignatureValues.unit)
fun IrClass.isKotlinArray() = this.isClassTypeWithSignature(IdSignatureValues.array)
val IrClass.superClasses get() = this.superTypes.map { it.classifierOrFail as IrClassSymbol }
fun IrClass.getSuperClassNotAny() = this.superClasses.map { it.owner }.atMostOne { !it.isInterface && !it.isAny() }
fun IrClass.isAny() = this.isClassTypeWithSignature(IdSignatureValues.any)
fun IrClass.isNothing() = this.isClassTypeWithSignature(IdSignatureValues.nothing)
fun IrClass.getSuperInterfaces() = this.superClasses.map { it.owner }.filter { it.isInterface }
// Note: psi2ir doesn't set `origin = FAKE_OVERRIDE` for fields and properties yet.
val IrProperty.isReal: Boolean get() = this.descriptor.kind.isReal
val IrField.isReal: Boolean get() = this.descriptor.kind.isReal
val IrSimpleFunction.isOverridable: Boolean
get() = visibility != DescriptorVisibilities.PRIVATE
&& modality != Modality.FINAL
&& (parent as? IrClass)?.isFinalClass != true
val IrFunction.isOverridable get() = this is IrSimpleFunction && this.isOverridable
val IrFunction.isOverridableOrOverrides
get() = this is IrSimpleFunction && (this.isOverridable || this.overriddenSymbols.isNotEmpty())
val IrClass.isFinalClass: Boolean
get() = modality == Modality.FINAL
fun IrClass.isSpecialClassWithNoSupertypes() = this.isAny() || this.isNothing()
inline fun <reified T> IrDeclaration.getAnnotationArgumentValue(fqName: FqName, argumentName: String): T? {
val annotation = this.annotations.findAnnotation(fqName) ?: return null
for (index in 0 until annotation.valueArgumentsCount) {
val parameter = annotation.symbol.owner.valueParameters[index]
if (parameter.name == Name.identifier(argumentName)) {
val actual = annotation.getValueArgument(index).safeAs<IrConst<T>>()
return actual?.value
}
}
return null
}
fun IrValueParameter.isInlineParameter(): Boolean =
!this.isNoinline && (this.type.isFunction() || this.type.isSuspendFunction()) && !this.type.isMarkedNullable()
val IrDeclaration.parentDeclarationsWithSelf: Sequence<IrDeclaration>
get() = generateSequence(this, { it.parent as? IrDeclaration })
fun IrClass.companionObject() = this.declarations.filterIsInstance<IrClass>().atMostOne { it.isCompanion }
fun buildSimpleAnnotation(irBuiltIns: IrBuiltIns, startOffset: Int, endOffset: Int,
annotationClass: IrClass, vararg args: String): IrConstructorCall {
val constructor = annotationClass.constructors.let {
it.singleOrNull() ?: it.single { ctor -> ctor.valueParameters.size == args.size }
}
return IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, constructor.returnType, constructor.symbol).apply {
args.forEachIndexed { index, arg ->
assert(constructor.valueParameters[index].type == irBuiltIns.stringType) {
"String type expected but was ${constructor.valueParameters[index].type}"
}
putValueArgument(index, IrConstImpl.string(startOffset, endOffset, irBuiltIns.stringType, arg))
}
}
}
internal fun IrExpression.isBoxOrUnboxCall() =
(this is IrCall && symbol.owner.origin == DECLARATION_ORIGIN_INLINE_CLASS_SPECIAL_FUNCTION)
val ModuleDescriptor.konanLibrary get() = (this.klibModuleOrigin as? DeserializedKlibModuleOrigin)?.library
val IrModuleFragment.konanLibrary get() =
(this as? KonanIrModuleFragmentImpl)?.konanLibrary ?: descriptor.konanLibrary
val IrFile.konanLibrary get() =
(metadata as? KonanFileMetadataSource)?.module?.konanLibrary ?: packageFragmentDescriptor.containingDeclaration.konanLibrary
val IrDeclaration.konanLibrary: KotlinLibrary? get() {
((this as? IrMetadataSourceOwner)?.metadata as? KonanMetadata)?.let { return it.konanLibrary }
val result = when (val parent = parent) {
is IrFile -> parent.konanLibrary
is IrPackageFragment -> parent.packageFragmentDescriptor.containingDeclaration.konanLibrary
is IrDeclaration -> parent.konanLibrary
else -> TODO("Unexpected declaration parent: $parent")
}
if (this is IrMetadataSourceOwner && this !is IrLazyDeclarationBase)
metadata = KonanMetadata(metadata?.name, result)
return result
}
fun IrDeclaration.isFromInteropLibrary() = konanLibrary?.isInteropLibrary() == true
@@ -0,0 +1,189 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.IrBuilder
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
internal inline fun <reified T: DeclarationDescriptor> ClassDescriptor.findDeclarationByName(name: String): T? =
unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<T>()
.firstOrNull { it.name.identifier == name }
/**
* Provides a set of functions and properties that helps
* to translate descriptor declarations to corresponding IR.
*/
internal interface DescriptorToIrTranslationMixin {
val symbolTable: SymbolTable
val irBuiltIns: IrBuiltIns
val typeTranslator: TypeTranslator
val postLinkageSteps: MutableList<() -> Unit>
fun invokePostLinkageSteps() {
postLinkageSteps.forEach { it() }
}
fun KotlinType.toIrType() = typeTranslator.translateType(this)
/**
* Declares [IrClass] instance from [descriptor] and populates it with
* supertypes, <this> parameter declaration and fake overrides.
* Additional elements are passed via [builder] callback.
*/
fun createClass(descriptor: ClassDescriptor, builder: (IrClass) -> Unit): IrClass =
symbolTable.declareClass(descriptor) {
symbolTable.irFactory.createIrClassFromDescriptor(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, it, descriptor
)
}.also { irClass ->
symbolTable.withScope(irClass) {
irClass.superTypes += descriptor.typeConstructor.supertypes.map {
it.toIrType()
}
irClass.generateAnnotations()
irClass.createParameterDeclarations()
builder(irClass)
createFakeOverrides(descriptor).forEach(irClass::addMember)
}
}
private fun createFakeOverrides(classDescriptor: ClassDescriptor): List<IrDeclaration> {
val fakeOverrides = classDescriptor.unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
return fakeOverrides.map {
when (it) {
is PropertyDescriptor -> createProperty(it)
is FunctionDescriptor -> createFunction(it, IrDeclarationOrigin.FAKE_OVERRIDE)
else -> error("Unexpected fake override descriptor: $it")
}
}
}
fun createConstructor(constructorDescriptor: ClassConstructorDescriptor): IrConstructor {
val irConstructor = symbolTable.declareConstructor(constructorDescriptor) {
with(constructorDescriptor) {
IrConstructorImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, it, name, visibility,
IrUninitializedType, isInline, isEffectivelyExternal(), isPrimary, isExpect
)
}
}
irConstructor.valueParameters += constructorDescriptor.valueParameters.map { valueParameterDescriptor ->
symbolTable.declareValueParameter(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.DEFINED,
valueParameterDescriptor,
valueParameterDescriptor.type.toIrType()).also {
it.parent = irConstructor
}
}
irConstructor.returnType = constructorDescriptor.returnType.toIrType()
irConstructor.generateAnnotations()
return irConstructor
}
fun createProperty(propertyDescriptor: PropertyDescriptor): IrProperty {
val origin = if (propertyDescriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
IrDeclarationOrigin.FAKE_OVERRIDE
} else {
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
}
val irProperty = symbolTable.declareProperty(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, origin, propertyDescriptor)
irProperty.getter = propertyDescriptor.getter?.let {
val irGetter = createFunction(it, origin)
irGetter.correspondingPropertySymbol = irProperty.symbol
irGetter
}
irProperty.setter = propertyDescriptor.setter?.let {
val irSetter = createFunction(it, origin)
irSetter.correspondingPropertySymbol = irProperty.symbol
irSetter
}
irProperty.generateAnnotations()
return irProperty
}
fun createFunction(
functionDescriptor: FunctionDescriptor,
origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
): IrSimpleFunction {
val irFunction = symbolTable.declareSimpleFunctionWithOverrides(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, origin, functionDescriptor)
symbolTable.withScope(irFunction) {
irFunction.returnType = functionDescriptor.returnType!!.toIrType()
irFunction.valueParameters += functionDescriptor.valueParameters.map {
symbolTable.declareValueParameter(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.DEFINED, it, it.type.toIrType())
}
irFunction.dispatchReceiverParameter = functionDescriptor.dispatchReceiverParameter?.let {
symbolTable.declareValueParameter(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.DEFINED, it, it.type.toIrType())
}
irFunction.generateAnnotations()
}
return irFunction
}
private fun IrDeclaration.generateAnnotations() {
annotations += descriptor.annotations.map {
typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(it)!!
}
}
}
internal fun IrBuilder.irInstanceInitializer(classSymbol: IrClassSymbol): IrExpression =
IrInstanceInitializerCallImpl(
startOffset, endOffset,
classSymbol,
context.irBuiltIns.unitType
)
internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): Boolean =
interopBuiltIns.cEnum in this.getSuperInterfaces()
internal fun ClassDescriptor.inheritsFromCStructVar(interopBuiltIns: InteropBuiltIns): Boolean =
interopBuiltIns.cStructVar == this.getSuperClassNotAny()
/**
* All enums that come from interop library implement CEnum interface.
* This function checks that given symbol located in subtree of
* CEnum inheritor.
*/
internal fun IrSymbol.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
descriptor.findCEnumDescriptor(interopBuiltIns)
internal fun DeclarationDescriptor.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
parentsWithSelf.filterIsInstance<ClassDescriptor>().firstOrNull { it.implementsCEnum(interopBuiltIns) }
/**
* All structs that come from interop library inherit from CStructVar class.
* This function checks that given symbol located in subtree of
* CStructVar inheritor.
*/
internal fun IrSymbol.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
descriptor.findCStructDescriptor(interopBuiltIns)
internal fun DeclarationDescriptor.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? =
parentsWithSelf.filterIsInstance<ClassDescriptor>().firstOrNull { it.inheritsFromCStructVar(interopBuiltIns) }
@@ -0,0 +1,119 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumByValueFunctionGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumClassGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumCompanionGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumVarClassGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cstruct.CStructVarClassGenerator
import org.jetbrains.kotlin.backend.konan.ir.interop.cstruct.CStructVarCompanionGenerator
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
/**
* For the most of descriptors that come from metadata-based interop libraries
* we generate a lazy IR.
* We use a different approach for CEnums and CStructVars and generate IR eagerly. Motivation:
* 1. CEnums are "real" Kotlin enums. Thus, we need apply the same compilation approach
* as we use for usual Kotlin enums.
* Eager generation allows to reuse [EnumClassLowering], [EnumConstructorsLowering] and other
* compiler phases.
* 2. It is an easier and more obvious approach. Since implementation of metadata-based
* libraries generation already took too much time we take an easier approach here.
*/
internal class IrProviderForCEnumAndCStructStubs(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns,
symbols: KonanSymbols
) {
/**
* TODO: integrate this provider into [KonanIrLinker.KonanInteropModuleDeserializer]
*/
private val symbolTable: SymbolTable = context.symbolTable
private val cEnumByValueFunctionGenerator =
CEnumByValueFunctionGenerator(context, symbols)
private val cEnumCompanionGenerator =
CEnumCompanionGenerator(context, cEnumByValueFunctionGenerator)
private val cEnumVarClassGenerator =
CEnumVarClassGenerator(context, interopBuiltIns)
private val cEnumClassGenerator =
CEnumClassGenerator(context, cEnumCompanionGenerator, cEnumVarClassGenerator)
private val cStructCompanionGenerator =
CStructVarCompanionGenerator(context, interopBuiltIns)
private val cStructClassGenerator =
CStructVarClassGenerator(context, interopBuiltIns, cStructCompanionGenerator)
fun isCEnumOrCStruct(declarationDescriptor: DeclarationDescriptor): Boolean =
declarationDescriptor.run { findCEnumDescriptor(interopBuiltIns) ?: findCStructDescriptor(interopBuiltIns) } != null
fun referenceAllEnumsAndStructsFrom(interopModule: ModuleDescriptor) = interopModule.getPackageFragments()
.flatMap { it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) }
.filterIsInstance<ClassDescriptor>()
.filter { it.implementsCEnum(interopBuiltIns) || it.inheritsFromCStructVar(interopBuiltIns) }
.forEach { symbolTable.referenceClass(it) }
private fun generateIrIfNeeded(symbol: IrSymbol, file: IrFile) {
// TODO: These `findOrGenerate` calls generate a whole subtree.
// This a simple but clearly suboptimal solution.
symbol.findCEnumDescriptor(interopBuiltIns)?.let { enumDescriptor ->
cEnumClassGenerator.findOrGenerateCEnum(enumDescriptor, file)
}
symbol.findCStructDescriptor(interopBuiltIns)?.let { structDescriptor ->
cStructClassGenerator.findOrGenerateCStruct(structDescriptor, file)
}
}
/**
* We postpone generation of bodies until IR linkage is complete.
* This way we ensure that all used symbols are resolved.
*/
fun generateBodies() {
cEnumCompanionGenerator.invokePostLinkageSteps()
cEnumByValueFunctionGenerator.invokePostLinkageSteps()
cEnumClassGenerator.invokePostLinkageSteps()
cEnumVarClassGenerator.invokePostLinkageSteps()
cStructClassGenerator.invokePostLinkageSteps()
cStructCompanionGenerator.invokePostLinkageSteps()
}
fun getDeclaration(descriptor: DeclarationDescriptor, idSignature: IdSignature, file: IrFile, symbolKind: BinarySymbolData.SymbolKind): IrSymbolOwner {
return symbolTable.run {
when (symbolKind) {
BinarySymbolData.SymbolKind.CLASS_SYMBOL -> declareClassFromLinker(descriptor as ClassDescriptor, idSignature) { s ->
generateIrIfNeeded(s, file)
s.owner
}
BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL -> declareEnumEntryFromLinker(descriptor as ClassDescriptor, idSignature) { s ->
generateIrIfNeeded(s, file)
s.owner
}
BinarySymbolData.SymbolKind.FUNCTION_SYMBOL -> declareSimpleFunctionFromLinker(descriptor as FunctionDescriptor, idSignature) { s ->
generateIrIfNeeded(s, file)
s.owner
}
BinarySymbolData.SymbolKind.PROPERTY_SYMBOL -> declarePropertyFromLinker(descriptor as PropertyDescriptor, idSignature) { s ->
generateIrIfNeeded(s, file)
s.owner
}
else -> error("Unexpected symbol kind $symbolKind for sig $idSignature")
}
}
}
companion object {
const val cTypeDefinitionsFileName = "CTypeDefinitions"
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cenum
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.findDeclarationByName
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.util.OperatorNameConventions
/**
* Generate IR for function that returns appropriate enum entry for the provided integral value.
*/
internal class CEnumByValueFunctionGenerator(
context: GeneratorContext,
private val symbols: KonanSymbols
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
fun generateByValueFunction(
companionIrClass: IrClass,
valuesIrFunctionSymbol: IrSimpleFunctionSymbol
): IrFunction {
val byValueFunctionDescriptor = companionIrClass.descriptor.findDeclarationByName<FunctionDescriptor>("byValue")!!
val byValueIrFunction = createFunction(byValueFunctionDescriptor)
val irValueParameter = byValueIrFunction.valueParameters.first()
// val values: Array<E> = values()
// var i: Int = 0
// val size: Int = values.size
// while (i < size) {
// val entry: E = values[i]
// if (entry.value == arg) {
// return entry
// }
// i++
// }
// throw NPE
postLinkageSteps.add {
byValueIrFunction.body = irBuilder(irBuiltIns, byValueIrFunction.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(irBlock {
val values = irTemporary(irCall(valuesIrFunctionSymbol), isMutable = true)
val inductionVariable = irTemporary(irInt(0), isMutable = true)
val arrayClass = values.type.classOrNull!!
val valuesSize = irCall(symbols.arraySize.getValue(arrayClass), irBuiltIns.intType).also { irCall ->
irCall.dispatchReceiver = irGet(values)
}
val getElementFn = symbols.arrayGet.getValue(arrayClass)
val plusFun = symbols.getBinaryOperator(OperatorNameConventions.PLUS, irBuiltIns.intType, irBuiltIns.intType)
val lessFunctionSymbol = irBuiltIns.lessFunByOperandType.getValue(irBuiltIns.intClass)
+irWhile().also { loop ->
loop.condition = irCall(lessFunctionSymbol, irBuiltIns.booleanType).also { irCall ->
irCall.putValueArgument(0, irGet(inductionVariable))
irCall.putValueArgument(1, valuesSize)
}
loop.body = irBlock {
val entry = irTemporary(irCall(getElementFn, byValueIrFunction.returnType).also { irCall ->
irCall.dispatchReceiver = irGet(values)
irCall.putValueArgument(0, irGet(inductionVariable))
}, isMutable = true)
val valueGetter = entry.type.getClass()!!.getPropertyGetter("value")!!
val entryValue = irGet(irValueParameter.type, irGet(entry), valueGetter)
+irIfThenElse(
type = irBuiltIns.unitType,
condition = irEquals(entryValue, irGet(irValueParameter)),
thenPart = irReturn(irGet(entry)),
elsePart = irSetVar(
inductionVariable,
irCallOp(plusFun, irBuiltIns.intType,
irGet(inductionVariable),
irInt(1)
)
)
)
}
}
+IrCallImpl.fromSymbolOwner(startOffset, endOffset, irBuiltIns.nothingType,
symbols.throwNullPointerException)
})
}
}
return byValueIrFunction
}
}
@@ -0,0 +1,178 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cenum
import org.jetbrains.kotlin.backend.konan.descriptors.enumEntries
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.findDeclarationByName
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrEnumConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.DeclarationGenerator
import org.jetbrains.kotlin.psi2ir.generators.EnumClassMembersGenerator
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
private fun extractConstantValue(descriptor: DeclarationDescriptor, type: String): ConstantValue<*>? =
descriptor.annotations
.findAnnotation(cEnumEntryValueAnnotationName.child(Name.identifier(type)))
?.allValueArguments
?.getValue(Name.identifier("value"))
private val cEnumEntryValueAnnotationName = FqName("kotlinx.cinterop.internal.ConstantValue")
private val cEnumEntryValueTypes = setOf(
"Byte", "Short", "Int", "Long",
"UByte", "UShort", "UInt", "ULong"
)
internal class CEnumClassGenerator(
val context: GeneratorContext,
private val cEnumCompanionGenerator: CEnumCompanionGenerator,
private val cEnumVarClassGenerator: CEnumVarClassGenerator
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
private val enumClassMembersGenerator = EnumClassMembersGenerator(DeclarationGenerator(context))
/**
* Searches for an IR class for [classDescriptor] in symbol table.
* Generates one if absent.
*/
fun findOrGenerateCEnum(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass {
val irClassSymbol = symbolTable.referenceClass(classDescriptor)
return if (!irClassSymbol.isBound) {
provideIrClassForCEnum(classDescriptor).also {
it.patchDeclarationParents(parent)
parent.declarations += it
}
} else {
irClassSymbol.owner
}
}
/**
* The main function that for given [descriptor] of the enum generates the whole
* IR tree including entries, CEnumVar class, and companion objects.
*/
private fun provideIrClassForCEnum(descriptor: ClassDescriptor): IrClass =
createClass(descriptor) { enumIrClass ->
enumIrClass.addMember(createEnumPrimaryConstructor(descriptor))
enumIrClass.addMember(createValueProperty(enumIrClass))
descriptor.enumEntries.mapTo(enumIrClass.declarations) { entryDescriptor ->
createEnumEntry(descriptor, entryDescriptor)
}
enumClassMembersGenerator.generateSpecialMembers(enumIrClass)
enumIrClass.addChild(cEnumCompanionGenerator.generate(enumIrClass))
enumIrClass.addChild(cEnumVarClassGenerator.generate(enumIrClass))
}
/**
* Creates `value` property that stores integral value of the enum.
*/
private fun createValueProperty(irClass: IrClass): IrProperty {
val propertyDescriptor = irClass.descriptor
.findDeclarationByName<PropertyDescriptor>("value")
?: error("No `value` property in ${irClass.name}")
val irProperty = createProperty(propertyDescriptor)
symbolTable.withScope(irProperty) {
irProperty.backingField = symbolTable.declareField(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
propertyDescriptor, propertyDescriptor.type.toIrType(), DescriptorVisibilities.PRIVATE
).also {
postLinkageSteps.add {
it.initializer = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run {
irExprBody(irGet(irClass.primaryConstructor!!.valueParameters[0]))
}
}
}
}
val getter = irProperty.getter!!
getter.correspondingPropertySymbol = irProperty.symbol
postLinkageSteps.add {
getter.body = irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(
irGetField(
irGet(getter.dispatchReceiverParameter!!),
irProperty.backingField!!
)
)
}
}
return irProperty
}
private fun createEnumEntry(enumDescriptor: ClassDescriptor, entryDescriptor: ClassDescriptor): IrEnumEntry {
val enumEntry = symbolTable.declareEnumEntry(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, entryDescriptor
)
val constructorSymbol = symbolTable.referenceConstructor(enumDescriptor.unsubstitutedPrimaryConstructor!!)
postLinkageSteps.add {
enumEntry.initializerExpression = IrExpressionBodyImpl(IrEnumConstructorCallImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
type = irBuiltIns.unitType,
symbol = constructorSymbol,
typeArgumentsCount = 0,
valueArgumentsCount = constructorSymbol.owner.valueParameters.size
).also {
it.putValueArgument(0, extractEnumEntryValue(entryDescriptor))
})
}
return enumEntry
}
/**
* Every enum entry that came from metadata-based interop library is annotated with
* [kotlinx.cinterop.internal.ConstantValue] annotation that holds internal constant value of the
* corresponding entry.
*
* This function extracts value from the annotation.
*/
private fun extractEnumEntryValue(entryDescriptor: ClassDescriptor): IrExpression =
cEnumEntryValueTypes.firstNotNullResult { extractConstantValue(entryDescriptor, it) } ?.let {
context.constantValueGenerator.generateConstantValueAsExpression(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, it)
} ?: error("Enum entry $entryDescriptor has no appropriate @$cEnumEntryValueAnnotationName annotation!")
private fun createEnumPrimaryConstructor(descriptor: ClassDescriptor): IrConstructor {
val irConstructor = createConstructor(descriptor.unsubstitutedPrimaryConstructor!!)
val enumConstructor = context.builtIns.enum.constructors.single()
val constructorSymbol = symbolTable.referenceConstructor(enumConstructor)
val classSymbol = symbolTable.referenceClass(descriptor)
val type = descriptor.defaultType.toIrType()
postLinkageSteps.add {
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
.irBlockBody {
+IrEnumConstructorCallImpl(
startOffset, endOffset,
context.irBuiltIns.unitType,
constructorSymbol,
typeArgumentsCount = 1, // kotlin.Enum<T> has a single type parameter.
valueArgumentsCount = constructorSymbol.owner.valueParameters.size
).apply {
putTypeArgument(0, type)
}
+irInstanceInitializer(classSymbol)
}
}
return irConstructor
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cenum
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
private val cEnumEntryAliasAnnonation = FqName("kotlinx.cinterop.internal.CEnumEntryAlias")
internal class CEnumCompanionGenerator(
context: GeneratorContext,
private val cEnumByValueFunctionGenerator: CEnumByValueFunctionGenerator
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
// Depends on already generated `.values()` irFunction.
fun generate(enumClass: IrClass): IrClass =
createClass(enumClass.descriptor.companionObjectDescriptor!!) { companionIrClass ->
companionIrClass.superTypes += irBuiltIns.anyType
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor))
val valuesFunction = enumClass.functions.single { it.name.identifier == "values" }.symbol
val byValueIrFunction = cEnumByValueFunctionGenerator
.generateByValueFunction(companionIrClass, valuesFunction)
companionIrClass.addMember(byValueIrFunction)
findEntryAliases(companionIrClass.descriptor)
.map { declareEntryAliasProperty(it, enumClass) }
.forEach(companionIrClass::addMember)
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor): IrConstructor {
val anyPrimaryConstructor = companionObjectDescriptor.builtIns.any.unsubstitutedPrimaryConstructor!!
val superConstructorSymbol = symbolTable.referenceConstructor(anyPrimaryConstructor)
val classSymbol = symbolTable.referenceClass(companionObjectDescriptor)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also {
postLinkageSteps.add {
it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
)
+irInstanceInitializer(classSymbol)
}
}
}
}
/**
* Returns all properties in companion object that represent aliases to
* enum entries.
*/
private fun findEntryAliases(companionDescriptor: ClassDescriptor) =
companionDescriptor.defaultType.memberScope.getContributedDescriptors()
.filterIsInstance<PropertyDescriptor>()
.filter { it.annotations.hasAnnotation(cEnumEntryAliasAnnonation) }
private fun fundCorrespondingEnumEntrySymbol(aliasDescriptor: PropertyDescriptor, irClass: IrClass): IrEnumEntrySymbol {
val enumEntryName = aliasDescriptor.annotations
.findAnnotation(cEnumEntryAliasAnnonation)!!
.getArgumentValueOrNull<String>("entryName")
return irClass.declarations.filterIsInstance<IrEnumEntry>()
.single { it.name.identifier == enumEntryName }.symbol
}
private fun generateAliasGetterBody(getter: IrSimpleFunction, entrySymbol: IrEnumEntrySymbol, enumClass: IrClass): IrBody =
irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+irReturn(
IrGetEnumValueImpl(startOffset, endOffset, enumClass.defaultType, entrySymbol)
)
}
private fun declareEntryAliasProperty(propertyDescriptor: PropertyDescriptor, enumClass: IrClass): IrProperty {
val entrySymbol = fundCorrespondingEnumEntrySymbol(propertyDescriptor, enumClass)
return createProperty(propertyDescriptor).also {
postLinkageSteps.add {
it.getter!!.body = generateAliasGetterBody(it.getter!!, entrySymbol, enumClass)
}
}
}
}
@@ -0,0 +1,99 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cenum
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.addMember
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
private val typeSizeAnnotation = FqName("kotlinx.cinterop.internal.CEnumVarTypeSize")
internal class CEnumVarClassGenerator(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
fun generate(enumIrClass: IrClass): IrClass {
val enumVarClassDescriptor = enumIrClass.descriptor.unsubstitutedMemberScope
.getContributedClassifier(Name.identifier("Var"), NoLookupLocation.FROM_BACKEND)!! as ClassDescriptor
return createClass(enumVarClassDescriptor) { enumVarClass ->
enumVarClass.addMember(createPrimaryConstructor(enumVarClass))
enumVarClass.addMember(createCompanionObject(enumVarClass))
enumVarClass.addMember(createValueProperty(enumVarClass))
}
}
private fun createValueProperty(enumVarClass: IrClass): IrProperty {
val valuePropertyDescriptor = enumVarClass.descriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("value"), NoLookupLocation.FROM_BACKEND).single()
return createProperty(valuePropertyDescriptor)
}
private fun createPrimaryConstructor(enumVarClass: IrClass): IrConstructor {
val irConstructor = createConstructor(enumVarClass.descriptor.unsubstitutedPrimaryConstructor!!)
val enumVarConstructorSymbol = symbolTable.referenceConstructor(
interopBuiltIns.cEnumVar.unsubstitutedPrimaryConstructor!!
)
val classSymbol = symbolTable.referenceClass(enumVarClass.descriptor)
postLinkageSteps.add {
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset, context.irBuiltIns.unitType, enumVarConstructorSymbol
).also {
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
}
+irInstanceInitializer(classSymbol)
}
}
return irConstructor
}
private fun createCompanionObject(enumVarClass: IrClass): IrClass =
createClass(enumVarClass.descriptor.companionObjectDescriptor!!) { companionIrClass ->
val typeSize = companionIrClass.descriptor.annotations
.findAnnotation(typeSizeAnnotation)!!
.getArgumentValueOrNull<Int>("size")!!
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, typeSize))
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, typeSize: Int): IrConstructor {
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cPrimitiveVarType.unsubstitutedPrimaryConstructor!!)
val classSymbol = symbolTable.referenceClass(companionObjectDescriptor)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also {
postLinkageSteps.add {
it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
).also {
it.putValueArgument(0, irInt(typeSize))
}
+irInstanceInitializer(classSymbol)
}
}
}
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.addMember
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
internal class CStructVarClassGenerator(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns,
private val companionGenerator: CStructVarCompanionGenerator
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
fun findOrGenerateCStruct(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass {
val irClassSymbol = symbolTable.referenceClass(classDescriptor)
return if (!irClassSymbol.isBound) {
provideIrClassForCStruct(classDescriptor).also {
it.patchDeclarationParents(parent)
parent.declarations += it
}
} else {
irClassSymbol.owner
}
}
private fun provideIrClassForCStruct(descriptor: ClassDescriptor): IrClass =
createClass(descriptor) { irClass ->
irClass.addMember(createPrimaryConstructor(irClass))
irClass.addMember(companionGenerator.generate(descriptor))
descriptor.unsubstitutedMemberScope
.getContributedDescriptors()
.filterIsInstance<PropertyDescriptor>()
.filter { it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
.map(this::createProperty)
.forEach(irClass::addMember)
}
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
val enumVarConstructorSymbol = symbolTable.referenceConstructor(
interopBuiltIns.cStructVar.unsubstitutedPrimaryConstructor!!
)
return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
postLinkageSteps.add {
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset,
context.irBuiltIns.unitType, enumVarConstructorSymbol
).also {
it.putValueArgument(0, irGet(irConstructor.valueParameters[0]))
}
+irInstanceInitializer(symbolTable.referenceClass(irClass.descriptor))
}
}
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.ir.interop.cstruct
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull
import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin
import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irLong
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.addMember
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irBuilder
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
private val varTypeAnnotationFqName = FqName("kotlinx.cinterop.internal.CStruct.VarType")
internal class CStructVarCompanionGenerator(
context: GeneratorContext,
private val interopBuiltIns: InteropBuiltIns
) : DescriptorToIrTranslationMixin {
override val irBuiltIns: IrBuiltIns = context.irBuiltIns
override val symbolTable: SymbolTable = context.symbolTable
override val typeTranslator: TypeTranslator = context.typeTranslator
override val postLinkageSteps: MutableList<() -> Unit> = mutableListOf()
fun generate(structDescriptor: ClassDescriptor): IrClass =
createClass(structDescriptor.companionObjectDescriptor!!) { companionIrClass ->
val annotation = companionIrClass.descriptor.annotations
.findAnnotation(varTypeAnnotationFqName)!!
val size = annotation.getArgumentValueOrNull<Long>("size")!!
val align = annotation.getArgumentValueOrNull<Int>("align")!!
companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, size, align))
}
private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor {
val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cStructVarType.unsubstitutedPrimaryConstructor!!)
return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor ->
postLinkageSteps.add {
irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset, context.irBuiltIns.unitType,
superConstructorSymbol
).also {
it.putValueArgument(0, irLong(size))
it.putValueArgument(1, irInt(align))
}
+irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor))
}
}
}
}
}
@@ -0,0 +1,153 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.ir.allParameters
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.ir.isUnit
import org.jetbrains.kotlin.backend.konan.isExternalObjCClass
import org.jetbrains.kotlin.backend.konan.isKotlinObjCClass
import org.jetbrains.kotlin.backend.konan.serialization.AbstractKonanIrMangler
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.name.Name
// This file describes the ABI for Kotlin descriptors of exported declarations.
// TODO: revise the naming scheme to ensure it produces unique names.
// TODO: do not serialize descriptors of non-exported declarations.
object KonanBinaryInterface {
private val mangler = object : AbstractKonanIrMangler(true) {}
private val exportChecker = mangler.getExportChecker()
val IrFunction.functionName: String get() = mangler.run { signatureString }
val IrFunction.symbolName: String get() = funSymbolNameImpl()
val IrField.symbolName: String get() =
withPrefix(MangleConstant.FIELD_PREFIX, fieldSymbolNameImpl())
val IrClass.typeInfoSymbolName: String get() =
withPrefix(MangleConstant.CLASS_PREFIX, typeInfoSymbolNameImpl())
fun isExported(declaration: IrDeclaration) = exportChecker.run {
check(declaration, SpecialDeclarationType.REGULAR) || declaration.isPlatformSpecificExported()
}
private fun withPrefix(prefix: String, mangle: String) = "$prefix:$mangle"
private fun IrFunction.funSymbolNameImpl(): String {
if (!isExported(this)) {
throw AssertionError(render())
}
if (isExternal) {
this.externalSymbolOrThrow()?.let {
return it
}
}
this.annotations.findAnnotation(RuntimeNames.exportForCppRuntime)?.let {
val name = it.getAnnotationStringValue() ?: this.name.asString()
return name // no wrapping currently required
}
return withPrefix(MangleConstant.FUN_PREFIX, mangler.run { mangleString })
}
private fun IrField.fieldSymbolNameImpl(): String {
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it."
}
return "$containingDeclarationPart$name"
}
private fun IrClass.typeInfoSymbolNameImpl(): String {
return this.fqNameForIrSerialization.toString()
}
}
internal val IrClass.writableTypeInfoSymbolName: String
get() {
assert (this.isExported())
return "ktypew:" + this.fqNameForIrSerialization.toString()
}
internal val IrClass.globalObjectStorageSymbolName: String
get() {
assert (this.isExported())
assert (this.kind.isSingleton)
assert (!this.isUnit())
return "kobjref:$fqNameForIrSerialization"
}
internal val IrClass.threadLocalObjectStorageGetterSymbolName: String
get() {
assert (this.isExported())
assert (this.kind.isSingleton)
assert (!this.isUnit())
return "kobjget:$fqNameForIrSerialization"
}
internal val IrClass.kotlinObjCClassInfoSymbolName: String
get() {
assert (this.isExported())
assert (this.isKotlinObjCClass())
return "kobjcclassinfo:$fqNameForIrSerialization"
}
fun IrFunction.computeFunctionName() = with(KonanBinaryInterface) { functionName }
fun IrFunction.computeFullName() = parent.fqNameForIrSerialization.child(Name.identifier(computeFunctionName())).asString()
fun IrFunction.computeSymbolName() = with(KonanBinaryInterface) { symbolName }.replaceSpecialSymbols()
fun IrField.computeSymbolName() = with(KonanBinaryInterface) { symbolName }.replaceSpecialSymbols()
fun IrClass.computeTypeInfoSymbolName() = with(KonanBinaryInterface) { typeInfoSymbolName }.replaceSpecialSymbols()
private fun String.replaceSpecialSymbols() =
// '@' is used for symbol versioning in GCC: https://gcc.gnu.org/wiki/SymbolVersioning.
this.replace("@", "__at__")
fun IrDeclaration.isExported() = KonanBinaryInterface.isExported(this)
// TODO: bring here dependencies of this method?
internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef {
val returnType = when {
function is IrConstructor -> voidType
function.isSuspend -> kObjHeaderPtr // Suspend functions return Any?.
else -> getLLVMReturnType(function.returnType)
}
val paramTypes = ArrayList(function.allParameters.map { getLLVMType(it.type) })
if (function.isSuspend)
paramTypes.add(kObjHeaderPtr) // Suspend functions have implicit parameter of type Continuation<>.
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
return functionType(returnType, isVarArg = false, paramTypes = paramTypes.toTypedArray())
}
internal val IrClass.typeInfoHasVtableAttached: Boolean
get() = !this.isAbstract() && !this.isExternalObjCClass()
internal val String.moduleConstructorName
get() = "_Konan_init_${this}"
internal val KonanLibrary.moduleConstructorName
get() = uniqueName.moduleConstructorName
@@ -0,0 +1,325 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.PhaserState
import org.jetbrains.kotlin.backend.common.phaser.namedUnitPhase
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis
import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
name = "ContextLLVMSetup",
description = "Set up Context for LLVM Bitcode generation",
op = { context, _ ->
// Note that we don't set module target explicitly.
// It is determined by the target of runtime.bc
// (see Llvm class in ContextUtils)
// Which in turn is determined by the clang flags
// used to compile runtime.bc.
llvmContext = LLVMContextCreate()!!
val llvmModule = LLVMModuleCreateWithNameInContext("out", llvmContext)!!
context.llvmModule = llvmModule
context.debugInfo.builder = LLVMCreateDIBuilder(llvmModule)
// we don't split path to filename and directory to provide enough level uniquely for dsymutil to avoid symbol
// clashing, which happens on linking with libraries produced from intercepting sources.
val filePath = context.config.outputFile.toFileAndFolder(context).path()
context.debugInfo.compilationUnit = if (context.shouldContainLocationDebugInfo()) DICreateCompilationUnit(
builder = context.debugInfo.builder,
lang = DWARF.language(context.config),
File = filePath,
dir = "",
producer = DWARF.producer,
isOptimized = 0,
flags = "",
rv = DWARF.runtimeVersion(context.config)).cast()
else null
}
)
internal val createLLVMDeclarationsPhase = makeKonanModuleOpPhase(
name = "CreateLLVMDeclarations",
description = "Map IR declarations to LLVM",
prerequisite = setOf(contextLLVMSetupPhase),
op = { context, _ ->
context.llvmDeclarations = createLlvmDeclarations(context)
context.lifetimes = mutableMapOf()
context.codegenVisitor = CodeGeneratorVisitor(context, context.lifetimes)
}
)
internal val disposeLLVMPhase = namedUnitPhase(
name = "DisposeLLVM",
description = "Dispose LLVM",
lower = object : CompilerPhase<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) {
context.disposeLlvm()
}
}
)
internal val freeNativeMemPhase = namedUnitPhase(
name = "FreeNativeMem",
description = "Free native memory used by interop",
lower = object : CompilerPhase<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) {
context.freeNativeMem()
}
}
)
internal val RTTIPhase = makeKonanModuleOpPhase(
name = "RTTI",
description = "RTTI generation",
op = { context, irModule ->
val visitor = RTTIGeneratorVisitor(context)
irModule.acceptVoid(visitor)
visitor.dispose()
}
)
internal val generateDebugInfoHeaderPhase = makeKonanModuleOpPhase(
name = "GenerateDebugInfoHeader",
description = "Generate debug info header",
op = { context, _ -> generateDebugInfoHeader(context) }
)
internal val buildDFGPhase = makeKonanModuleOpPhase(
name = "BuildDFG",
description = "Data flow graph building",
op = { context, irModule ->
context.moduleDFG = ModuleDFGBuilder(context, irModule).build()
}
)
internal val devirtualizationPhase = makeKonanModuleOpPhase(
name = "Devirtualization",
description = "Devirtualization",
prerequisite = setOf(buildDFGPhase),
op = { context, irModule ->
context.devirtualizationAnalysisResult = Devirtualization.run(
irModule, context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
)
}
)
internal val redundantCoercionsCleaningPhase = makeKonanModuleOpPhase(
name = "RedundantCoercionsCleaning",
description = "Redundant coercions cleaning",
op = { context, irModule -> irModule.files.forEach { RedundantCoercionsCleaner(context).lower(it) } }
)
internal val ghaPhase = makeKonanModuleOpPhase(
name = "GHAPhase",
description = "Global hierarchy analysis",
op = { context, irModule -> GlobalHierarchyAnalysis(context, irModule).run() }
)
internal val IrFunction.longName: String
get() = "${(parent as? IrClass)?.name?.asString() ?: "<root>"}.${(this as? IrSimpleFunction)?.name ?: "<init>"}"
internal val dcePhase = makeKonanModuleOpPhase(
name = "DCEPhase",
description = "Dead code elimination",
prerequisite = setOf(devirtualizationPhase),
op = { context, _ ->
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val callGraph = CallGraphBuilder(
context, context.moduleDFG!!,
externalModulesDFG,
context.devirtualizationAnalysisResult!!,
// For DCE we don't wanna miss any potentially reachable function.
nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE
).build()
val referencedFunctions = mutableSetOf<IrFunction>()
callGraph.rootExternalFunctions.forEach {
if (!it.isGlobalInitializer)
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
}
for (node in callGraph.directEdges.values) {
if (!node.symbol.isGlobalInitializer)
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
node.callSites.forEach {
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}"))
}
}
context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
// TODO: Generalize somehow, not that graceful.
if (declaration.name == OperatorNameConventions.INVOKE
&& declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) {
referencedFunctions.add(declaration)
}
super.visitFunction(declaration)
}
override fun visitConstructor(declaration: IrConstructor) {
// TODO: NativePointed is the only inline class for which the field's type and
// the constructor parameter's type are different.
// Thus we need to conserve the constructor no matter if it was actually referenced somehow or not.
// See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why.
if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary)
referencedFunctions.add(declaration)
super.visitConstructor(declaration)
}
})
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitFile(declaration: IrFile): IrFile {
declaration.declarations.removeAll {
(it is IrFunction && !referencedFunctions.contains(it))
}
return super.visitFile(declaration)
}
override fun visitClass(declaration: IrClass): IrStatement {
if (declaration == context.ir.symbols.nativePointed)
return super.visitClass(declaration)
declaration.declarations.removeAll {
(it is IrFunction && it.isReal && !referencedFunctions.contains(it))
}
return super.visitClass(declaration)
}
override fun visitProperty(declaration: IrProperty): IrStatement {
if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.getter = null
}
if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
declaration.setter = null
}
return super.visitProperty(declaration)
}
})
context.referencedFunctions = referencedFunctions
}
)
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
name = "EscapeAnalysis",
description = "Escape analysis",
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
op = { context, _ ->
val entryPoint = context.ir.symbols.entryPoint?.owner
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
val nonDevirtualizedCallSitesUnfoldFactor =
if (entryPoint != null) {
// For a final program it can be safely assumed that what classes we see is what we got,
// so can take those. In theory we can always unfold call sites using type hierarchy, but
// the analysis might converge much, much slower, so take only reasonably small for now.
5
}
else {
// Can't tolerate any non-devirtualized call site for a library.
// TODO: What about private virtual functions?
// Note: 0 is also bad - this means that there're no inheritors in the current source set,
// but there might be some provided by the users of the library being produced.
-1
}
val callGraph = CallGraphBuilder(
context, context.moduleDFG!!,
externalModulesDFG,
context.devirtualizationAnalysisResult!!,
nonDevirtualizedCallSitesUnfoldFactor
).build()
EscapeAnalysis.computeLifetimes(
context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes
)
}
)
internal val localEscapeAnalysisPhase = makeKonanModuleOpPhase(
name = "LocalEscapeAnalysis",
description = "Local escape analysis",
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
op = { context, _ ->
LocalEscapeAnalysis.computeLifetimes(context, context.moduleDFG!!, context.lifetimes)
}
)
internal val codegenPhase = makeKonanModuleOpPhase(
name = "Codegen",
description = "Code generation",
op = { context, irModule ->
irModule.acceptVoid(context.codegenVisitor)
}
)
internal val finalizeDebugInfoPhase = makeKonanModuleOpPhase(
name = "FinalizeDebugInfo",
description = "Finalize debug info",
op = { context, _ ->
if (context.shouldContainAnyDebugInfo()) {
DIFinalize(context.debugInfo.builder)
}
}
)
internal val cStubsPhase = makeKonanModuleOpPhase(
name = "CStubs",
description = "C stubs compilation",
op = { context, _ -> produceCStubs(context) }
)
internal val linkBitcodeDependenciesPhase = makeKonanModuleOpPhase(
name = "LinkBitcodeDependencies",
description = "Link bitcode dependencies",
op = { context, _ -> linkBitcodeDependencies(context) }
)
internal val bitcodeOptimizationPhase = makeKonanModuleOpPhase(
name = "BitcodeOptimization",
description = "Optimize bitcode",
op = { context, _ -> runLlvmOptimizationPipeline(context) }
)
internal val produceOutputPhase = namedUnitPhase(
name = "ProduceOutput",
description = "Produce output",
lower = object : CompilerPhase<Context, Unit, Unit> {
override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState<Unit>, context: Context, input: Unit) {
produceOutput(context)
}
}
)
internal val verifyBitcodePhase = makeKonanModuleOpPhase(
name = "VerifyBitcode",
description = "Verify bitcode",
op = { context, _ -> context.verifyBitCode() }
)
internal val printBitcodePhase = makeKonanModuleOpPhase(
name = "PrintBitcode",
description = "Print bitcode",
op = { context, _ ->
if (context.shouldPrintBitCode()) {
context.printBitCode()
}
}
)
@@ -0,0 +1,638 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import llvm.*
import org.jetbrains.kotlin.backend.konan.CachedLibraries
import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.hash.GlobalHash
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.cast
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
internal sealed class SlotType {
// An object is statically allocated on stack.
object STACK : SlotType()
// Frame local arena slot can be used.
object ARENA : SlotType()
// Return slot can be used.
object RETURN : SlotType()
// Return slot, if it is an arena, can be used.
object RETURN_IF_ARENA : SlotType()
// Param slot, if it is an arena, can be used.
class PARAM_IF_ARENA(val parameter: Int) : SlotType()
// Params slot, if it is an arena, can be used.
class PARAMS_IF_ARENA(val parameters: IntArray, val useReturnSlot: Boolean) : SlotType()
// Anonymous slot.
object ANONYMOUS : SlotType()
// Unknown slot type.
object UNKNOWN : SlotType()
}
// Lifetimes class of reference, computed by escape analysis.
internal sealed class Lifetime(val slotType: SlotType) {
object STACK : Lifetime(SlotType.STACK) {
override fun toString(): String {
return "STACK"
}
}
// If reference is frame-local (only obtained from some call and never leaves).
object LOCAL : Lifetime(SlotType.ARENA) {
override fun toString(): String {
return "LOCAL"
}
}
// If reference is only returned.
object RETURN_VALUE : Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "RETURN_VALUE"
}
}
// If reference is set as field of references of class RETURN_VALUE or INDIRECT_RETURN_VALUE.
object INDIRECT_RETURN_VALUE : Lifetime(SlotType.RETURN_IF_ARENA) {
override fun toString(): String {
return "INDIRECT_RETURN_VALUE"
}
}
// If reference is stored to the field of an incoming parameters.
class PARAMETER_FIELD(val parameter: Int) : Lifetime(SlotType.PARAM_IF_ARENA(parameter)) {
override fun toString(): String {
return "PARAMETER_FIELD($parameter)"
}
}
// If reference is stored to the field of an incoming parameters.
class PARAMETERS_FIELD(val parameters: IntArray, val useReturnSlot: Boolean)
: Lifetime(SlotType.PARAMS_IF_ARENA(parameters, useReturnSlot)) {
override fun toString(): String {
return "PARAMETERS_FIELD(${parameters.contentToString()}, useReturnSlot='$useReturnSlot')"
}
}
// If reference refers to the global (either global object or global variable).
object GLOBAL : Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "GLOBAL"
}
}
// If reference used to throw.
object THROW : Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "THROW"
}
}
// If reference used as an argument of outgoing function. Class can be improved by escape analysis
// of called function.
object ARGUMENT : Lifetime(SlotType.ANONYMOUS) {
override fun toString(): String {
return "ARGUMENT"
}
}
// If reference class is unknown.
object UNKNOWN : Lifetime(SlotType.UNKNOWN) {
override fun toString(): String {
return "UNKNOWN"
}
}
// If reference class is irrelevant.
object IRRELEVANT : Lifetime(SlotType.UNKNOWN) {
override fun toString(): String {
return "IRRELEVANT"
}
}
}
/**
* Provides utility methods to the implementer.
*/
internal interface ContextUtils : RuntimeAware {
val context: Context
override val runtime: Runtime
get() = context.llvm.runtime
/**
* Describes the target platform.
*
* TODO: using [llvmTargetData] usually results in generating non-portable bitcode.
*/
val llvmTargetData: LLVMTargetDataRef
get() = runtime.targetData
val staticData: StaticData
get() = context.llvm.staticData
/**
* TODO: maybe it'd be better to replace with [IrDeclaration::isEffectivelyExternal()],
* or just drop all [else] branches of corresponding conditionals.
*/
fun isExternal(declaration: IrDeclaration): Boolean {
return !context.llvmModuleSpecification.containsDeclaration(declaration)
}
/**
* LLVM function generated from the Kotlin function.
* It may be declared as external function prototype.
*/
val IrFunction.llvmFunction: LLVMValueRef
get() = llvmFunctionOrNull
?: error("$name in $file/${parent.fqNameForIrSerialization}")
val IrFunction.llvmFunctionOrNull: LLVMValueRef?
get() {
assert(this.isReal)
return if (isExternal(this)) {
runtime.addedLLVMExternalFunctions.getOrPut(this) { context.llvm.externalFunction(this.computeSymbolName(), getLlvmFunctionType(this),
origin = this.llvmSymbolOrigin) }
} else {
context.llvmDeclarations.forFunctionOrNull(this)?.llvmFunction
}
}
/**
* Address of entry point of [llvmFunction].
*/
val IrFunction.entryPointAddress: ConstPointer
get() {
val result = LLVMConstBitCast(this.llvmFunction, int8TypePtr)!!
return constPointer(result)
}
val IrClass.typeInfoPtr: ConstPointer
get() {
return if (isExternal(this)) {
constPointer(importGlobal(this.computeTypeInfoSymbolName(), runtime.typeInfoType,
origin = this.llvmSymbolOrigin))
} else {
context.llvmDeclarations.forClass(this).typeInfo
}
}
/**
* Pointer to type info for given class.
* It may be declared as pointer to external variable.
*/
val IrClass.llvmTypeInfoPtr: LLVMValueRef
get() = typeInfoPtr.llvm
/**
* Returns contents of this [GlobalHash].
*
* It must be declared identically with [Runtime.globalHashType].
*/
fun GlobalHash.getBytes(): ByteArray {
@Suppress("DEPRECATION")
val size = GlobalHash.size
assert(size == LLVMStoreSizeOfType(llvmTargetData, runtime.globalHashType))
return this.bits.getBytes(size)
}
/**
* Returns global hash of this string contents.
*/
val String.globalHashBytes: ByteArray
get() = memScoped {
val hash = globalHash(stringAsBytes(this@globalHashBytes), memScope)
hash.getBytes()
}
/**
* Return base64 representation for global hash of this string contents.
*/
val String.globalHashBase64: String
get() {
return base64Encode(globalHashBytes)
}
val String.globalHash: ConstValue
get() = memScoped {
val hashBytes = this@globalHash.globalHashBytes
return Struct(runtime.globalHashType, ConstArray(int8Type, hashBytes.map { Int8(it) }))
}
val FqName.globalHash: ConstValue
get() = this.toString().globalHash
}
/**
* Converts this string to the sequence of bytes to be used for hashing/storing to binary/etc.
*/
internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
internal val String.localHash: LocalHash
get() = LocalHash(localHash(stringAsBytes(this)))
internal val Name.localHash: LocalHash
get() = this.toString().localHash
internal val FqName.localHash: LocalHash
get() = this.toString().localHash
internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
if (LLVMGetNamedFunction(llvmModule, name) != null) {
throw IllegalArgumentException("function $name already exists")
}
val externalFunction = LLVMGetNamedFunction(otherModule, name) ?: throw Error("function $name not found")
val functionType = getFunctionType(externalFunction)
val function = LLVMAddFunction(llvmModule, name, functionType)!!
copyFunctionAttributes(externalFunction, function)
return function
}
private fun importGlobal(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
if (LLVMGetNamedGlobal(llvmModule, name) != null) {
throw IllegalArgumentException("global $name already exists")
}
val externalGlobal = LLVMGetNamedGlobal(otherModule, name)!!
val globalType = getGlobalType(externalGlobal)
val global = LLVMAddGlobal(llvmModule, globalType, name)!!
return global
}
private fun copyFunctionAttributes(source: LLVMValueRef, destination: LLVMValueRef) {
// TODO: consider parameter attributes
val attributeIndex = LLVMAttributeFunctionIndex
val count = LLVMGetAttributeCountAtIndex(source, attributeIndex)
memScoped {
val attributes = allocArray<LLVMAttributeRefVar>(count)
LLVMGetAttributesAtIndex(source, attributeIndex, attributes)
(0 until count).forEach {
LLVMAddAttributeAtIndex(destination, attributeIndex, attributes[it])
}
}
}
private fun importMemset(): LLVMValueRef {
val functionType = functionType(voidType, false, int8TypePtr, int8Type, int32Type, int1Type)
return llvmIntrinsic("llvm.memset.p0i8.i32", functionType)
}
private fun llvmIntrinsic(name: String, type: LLVMTypeRef, vararg attributes: String): LLVMValueRef {
val result = LLVMAddFunction(llvmModule, name, type)!!
attributes.forEach {
val kindId = getLlvmAttributeKindId(it)
addLlvmFunctionEnumAttribute(result, kindId)
}
return result
}
internal fun externalFunction(
name: String,
type: LLVMTypeRef,
origin: CompiledKlibModuleOrigin,
independent: Boolean = false
): LLVMValueRef {
this.imports.add(origin, onlyBitcode = independent)
val found = LLVMGetNamedFunction(llvmModule, name)
if (found != null) {
assert(getFunctionType(found) == type) {
"Expected: ${LLVMPrintTypeToString(type)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
}
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
return found
} else {
// As exported functions are written in C++ they assume sign extension for promoted types -
// mention that in attributes.
val function = addLlvmFunctionWithDefaultAttributes(context, llvmModule, name, type)
return memScoped {
val paramCount = LLVMCountParamTypes(type)
val paramTypes = allocArray<LLVMTypeRefVar>(paramCount)
LLVMGetParamTypes(type, paramTypes)
(0 until paramCount).forEach { index ->
val paramType = paramTypes[index]
addFunctionSignext(function, index + 1, paramType)
}
val returnType = LLVMGetReturnType(type)
addFunctionSignext(function, 0, returnType)
function
}
}
}
private fun externalNounwindFunction(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef {
val function = externalFunction(name, type, origin)
setFunctionNoUnwind(function)
return function
}
val imports get() = context.llvmImports
class ImportsImpl(private val context: Context) : LlvmImports {
private val usedBitcode = mutableSetOf<KotlinLibrary>()
private val usedNativeDependencies = mutableSetOf<KotlinLibrary>()
private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
override fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean) {
val library = when (origin) {
CurrentKlibModuleOrigin -> return
is DeserializedKlibModuleOrigin -> origin.library
}
if (library !in allLibraries) {
error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}")
}
usedBitcode.add(library)
if (!onlyBitcode) {
usedNativeDependencies.add(library)
}
}
override fun bitcodeIsUsed(library: KonanLibrary) = library in usedBitcode
override fun nativeDependenciesAreUsed(library: KonanLibrary) = library in usedNativeDependencies
}
val nativeDependenciesToLink: List<KonanLibrary> by lazy {
context.config.resolvedLibraries
.getFullList(TopologicalLibraryOrder)
.filter {
require(it is KonanLibrary)
(!it.isDefault && !context.config.purgeUserLibs) || imports.nativeDependenciesAreUsed(it)
}.cast<List<KonanLibrary>>()
}
private val immediateBitcodeDependencies: List<KonanLibrary> by lazy {
context.config.resolvedLibraries.getFullList(TopologicalLibraryOrder).cast<List<KonanLibrary>>()
.filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) }
}
val allCachedBitcodeDependencies: List<KonanLibrary> by lazy {
val allLibraries = context.config.resolvedLibraries.getFullList().associateBy { it.uniqueName }
val result = mutableSetOf<KonanLibrary>()
fun addDependencies(cachedLibrary: CachedLibraries.Cache) {
cachedLibrary.bitcodeDependencies.forEach {
val library = allLibraries[it] ?: error("Bitcode dependency to an unknown library: $it")
result.add(library as KonanLibrary)
addDependencies(context.config.cachedLibraries.getLibraryCache(library)
?: error("Library $it is expected to be cached"))
}
}
for (library in immediateBitcodeDependencies) {
val cache = context.config.cachedLibraries.getLibraryCache(library)
if (cache != null) {
result += library
addDependencies(cache)
}
}
result.toList()
}
val allNativeDependencies: List<KonanLibrary> by lazy {
(nativeDependenciesToLink + allCachedBitcodeDependencies).distinct()
}
val allBitcodeDependencies: List<KonanLibrary> by lazy {
val allNonCachedDependencies = context.librariesWithDependencies.filter {
context.config.cachedLibraries.getLibraryCache(it) == null
}
val set = (allNonCachedDependencies + allCachedBitcodeDependencies).toSet()
// This list is used in particular to build the libraries' initializers chain.
// The initializers must be called in the topological order, so make sure that the
// libraries list being returned is also toposorted.
context.config.resolvedLibraries
.getFullList(TopologicalLibraryOrder)
.cast<List<KonanLibrary>>()
.filter { it in set }
}
val bitcodeToLink: List<KonanLibrary> by lazy {
(context.config.resolvedLibraries.getFullList(TopologicalLibraryOrder).cast<List<KonanLibrary>>())
.filter { shouldContainBitcode(it) }
}
private fun shouldContainBitcode(library: KonanLibrary): Boolean {
if (!context.llvmModuleSpecification.containsLibrary(library)) {
return false
}
if (!context.llvmModuleSpecification.isFinal) {
return true
}
// Apply some DCE:
return (!library.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(library)
}
val additionalProducedBitcodeFiles = mutableListOf<String>()
val staticData = StaticData(context)
private val target = context.config.target
val runtimeFile = context.config.distribution.runtime(target)
val runtime = Runtime(runtimeFile) // TODO: dispose
val targetTriple = runtime.target
init {
LLVMSetDataLayout(llvmModule, runtime.dataLayout)
LLVMSetTarget(llvmModule, targetTriple)
}
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
private fun importRtGlobal(name: String) = importGlobal(name, runtime.llvmModule)
val allocInstanceFunction = importRtFunction("AllocInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val initThreadLocalSingleton = importRtFunction("InitThreadLocalSingleton")
val initSingletonFunction = importRtFunction("InitSingleton")
val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal")
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
val updateStackRefFunction = importRtFunction("UpdateStackRef")
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
val zeroHeapRefFunction = importRtFunction("ZeroHeapRef")
val zeroArrayRefsFunction = importRtFunction("ZeroArrayRefs")
val enterFrameFunction = importRtFunction("EnterFrame")
val leaveFrameFunction = importRtFunction("LeaveFrame")
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord")
val isInstanceFunction = importRtFunction("IsInstance")
val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast")
val throwExceptionFunction = importRtFunction("ThrowException")
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
val addTLSRecord = importRtFunction("AddTLSRecord")
val lookupTLS = importRtFunction("LookupTLS")
val initRuntimeIfNeeded = importRtFunction("Kotlin_initRuntimeIfNeeded")
val mutationCheck = importRtFunction("MutationCheck")
val checkLifetimesConstraint = importRtFunction("CheckLifetimesConstraint")
val freezeSubgraph = importRtFunction("FreezeSubgraph")
val checkGlobalsAccessible = importRtFunction("CheckGlobalsAccessible")
val Kotlin_getExceptionObject = importRtFunction("Kotlin_getExceptionObject")
val kRefSharedHolderInitLocal = importRtFunction("KRefSharedHolder_initLocal")
val kRefSharedHolderInit = importRtFunction("KRefSharedHolder_init")
val kRefSharedHolderDispose = importRtFunction("KRefSharedHolder_dispose")
val kRefSharedHolderRef = importRtFunction("KRefSharedHolder_ref")
val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") }
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
val missingInitImp by lazy { importRtFunction("MissingInitImp") }
val Kotlin_Interop_DoesObjectConformToProtocol by lazyRtFunction
val Kotlin_Interop_IsObjectKindOfClass by lazyRtFunction
val Kotlin_ObjCExport_refToObjC by lazyRtFunction
val Kotlin_ObjCExport_refFromObjC by lazyRtFunction
val Kotlin_ObjCExport_CreateNSStringFromKString by lazyRtFunction
val Kotlin_ObjCExport_convertUnit by lazyRtFunction
val Kotlin_ObjCExport_GetAssociatedObject by lazyRtFunction
val Kotlin_ObjCExport_AbstractMethodCalled by lazyRtFunction
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_AllocInstanceWithAssociatedObject by lazyRtFunction
val Kotlin_ObjCExport_createContinuationArgument by lazyRtFunction
val Kotlin_ObjCExport_resumeContinuation by lazyRtFunction
val Kotlin_mm_safePointFunctionEpilogue by lazyRtFunction
val Kotlin_mm_safePointWhileLoopBody by lazyRtFunction
val Kotlin_mm_safePointExceptionUnwind by lazyRtFunction
val tlsMode by lazy {
when (target) {
KonanTarget.WASM32,
is KonanTarget.ZEPHYR -> LLVMThreadLocalMode.LLVMNotThreadLocal
else -> LLVMThreadLocalMode.LLVMGeneralDynamicTLSModel
}
}
var tlsCount = 0
val tlsKey by lazy {
val global = LLVMAddGlobal(llvmModule, kInt8Ptr, "__KonanTlsKey")!!
LLVMSetLinkage(global, LLVMLinkage.LLVMInternalLinkage)
LLVMSetInitializer(global, LLVMConstNull(kInt8Ptr))
global
}
private val personalityFunctionName = when (target) {
KonanTarget.IOS_ARM32 -> "__gxx_personality_sj0"
KonanTarget.MINGW_X64 -> "__gxx_personality_seh0"
else -> "__gxx_personality_v0"
}
val cxxStdTerminate = externalNounwindFunction(
"_ZSt9terminatev", // mangled C++ 'std::terminate'
functionType(voidType, false),
origin = context.standardLlvmSymbolsOrigin
)
val gxxPersonalityFunction = externalNounwindFunction(
personalityFunctionName,
functionType(int32Type, true),
origin = context.standardLlvmSymbolsOrigin
)
val cxaBeginCatchFunction = externalNounwindFunction(
"__cxa_begin_catch",
functionType(int8TypePtr, false, int8TypePtr),
origin = context.standardLlvmSymbolsOrigin
)
val cxaEndCatchFunction = externalNounwindFunction(
"__cxa_end_catch",
functionType(voidType, false),
origin = context.standardLlvmSymbolsOrigin
)
val memsetFunction = importMemset()
//val memcpyFunction = importMemcpy()
val llvmTrap = llvmIntrinsic(
"llvm.trap",
functionType(voidType, false),
"cold", "noreturn", "nounwind"
)
val llvmEhTypeidFor = llvmIntrinsic(
"llvm.eh.typeid.for",
functionType(int32Type, false, int8TypePtr),
"nounwind", "readnone"
)
val usedFunctions = mutableListOf<LLVMValueRef>()
val usedGlobals = mutableListOf<LLVMValueRef>()
val compilerUsedGlobals = mutableListOf<LLVMValueRef>()
val irStaticInitializers = mutableListOf<IrStaticInitializer>()
val otherStaticInitializers = mutableListOf<LLVMValueRef>()
val fileInitializers = mutableListOf<IrField>()
var fileUsesThreadLocalObjects = false
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
private object lazyRtFunction {
operator fun provideDelegate(
thisRef: Llvm, property: KProperty<*>
) = object : ReadOnlyProperty<Llvm, LLVMValueRef> {
val value by lazy { thisRef.importRtFunction(property.name) }
override fun getValue(thisRef: Llvm, property: KProperty<*>): LLVMValueRef = value
}
}
val llvmInt8 = int8Type
val llvmInt16 = int16Type
val llvmInt32 = int32Type
val llvmInt64 = int64Type
val llvmFloat = floatType
val llvmDouble = doubleType
val llvmVector128 = vector128Type
}
class IrStaticInitializer(val konanLibrary: KotlinLibrary?, val initializer: LLVMValueRef)
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.isUnit
private fun RuntimeAware.getLlvmType(primitiveBinaryType: PrimitiveBinaryType?) = when (primitiveBinaryType) {
null -> this.kObjHeaderPtr
PrimitiveBinaryType.BOOLEAN -> int1Type
PrimitiveBinaryType.BYTE -> int8Type
PrimitiveBinaryType.SHORT -> int16Type
PrimitiveBinaryType.INT -> int32Type
PrimitiveBinaryType.LONG -> int64Type
PrimitiveBinaryType.FLOAT -> floatType
PrimitiveBinaryType.DOUBLE -> doubleType
PrimitiveBinaryType.VECTOR128 -> vector128Type
PrimitiveBinaryType.POINTER -> int8TypePtr
}
internal fun RuntimeAware.getLLVMType(type: IrType): LLVMTypeRef =
runtime.calculatedLLVMTypes.getOrPut(type) { getLlvmType(type.computePrimitiveBinaryTypeOrNull()) }
internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) =
getLlvmType(type.primitiveBinaryType)
internal fun IrType.isVoidAsReturnType() = isUnit() || isNothing()
internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef {
return when {
type.isVoidAsReturnType() -> voidType
else -> getLLVMType(type)
}
}
@@ -0,0 +1,292 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.allocArrayOf
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.reinterpret
import llvm.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.ir.SourceManager.FileEntry
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.isTypeParameter
import org.jetbrains.kotlin.ir.util.isUnsigned
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.utils.addToStdlib.cast
internal object DWARF {
val producer = "konanc ${CompilerVersion.CURRENT} / kotlin-compiler: ${KotlinVersion.CURRENT}"
/* TODO: from LLVM sources is unclear what runtimeVersion corresponds to term in terms of dwarf specification. */
val dwarfVersionMetaDataNodeName get() = "Dwarf Version".mdString()
val dwarfDebugInfoMetaDataNodeName get() = "Debug Info Version".mdString()
const val debugInfoVersion = 3 /* TODO: configurable? */
/**
* This is the value taken from [DIFlags.FlagFwdDecl], to mark type declaration as
* forward one.
*/
const val flagsForwardDeclaration = 4
fun runtimeVersion(config: KonanConfig) = when (config.debugInfoVersion()) {
2 -> 0
1 -> 2 /* legacy :/ */
else -> TODO("unsupported debug info format version")
}
/**
* Note: Kotlin language constant appears in DWARF v6, while modern linker fails to links DWARF other then [2;4],
* that why we emit version 4 actually.
*/
fun dwarfVersion(config : KonanConfig) = when (config.debugInfoVersion()) {
1 -> 2
2 -> 4 /* likely the most of the future kotlin native debug info format versions will emit DWARF v4 */
else -> TODO("unsupported debug info format version")
}
fun language(config: KonanConfig) = when (config.debugInfoVersion()) {
1 -> DwarfLanguage.DW_LANG_C89.value
else -> DwarfLanguage.DW_LANG_Kotlin.value
}
}
fun KonanConfig.debugInfoVersion():Int = configuration[KonanConfigKeys.DEBUG_INFO_VERSION] ?: 1
internal class DebugInfo internal constructor(override val context: Context):ContextUtils {
val files = mutableMapOf<String, DIFileRef>()
val subprograms = mutableMapOf<LLVMValueRef, DISubprogramRef>()
/* Some functions are inlined on all callsites and body is eliminated by DCE, so there's no LLVM value */
val inlinedSubprograms = mutableMapOf<IrFunction, DISubprogramRef>()
var builder: DIBuilderRef? = null
var module: DIModuleRef? = null
var compilationUnit: DIScopeOpaqueRef? = null
var objHeaderPointerType: DITypeOpaqueRef? = null
var types = mutableMapOf<IrType, DITypeOpaqueRef>()
val llvmTypes = mapOf<IrType, LLVMTypeRef>(
context.irBuiltIns.booleanType to context.llvm.llvmInt8,
context.irBuiltIns.byteType to context.llvm.llvmInt8,
context.irBuiltIns.charType to context.llvm.llvmInt16,
context.irBuiltIns.shortType to context.llvm.llvmInt16,
context.irBuiltIns.intType to context.llvm.llvmInt32,
context.irBuiltIns.longType to context.llvm.llvmInt64,
context.irBuiltIns.floatType to context.llvm.llvmFloat,
context.irBuiltIns.doubleType to context.llvm.llvmDouble)
val llvmTypeSizes = llvmTypes.map { it.key to LLVMSizeOfTypeInBits(llvmTargetData, it.value) }.toMap()
val llvmTypeAlignments = llvmTypes.map {it.key to LLVMPreferredAlignmentOfType(llvmTargetData, it.value)}.toMap()
val otherLlvmType = LLVMPointerType(int64Type, 0)!!
val otherTypeSize = LLVMSizeOfTypeInBits(llvmTargetData, otherLlvmType)
val otherTypeAlignment = LLVMPreferredAlignmentOfType(llvmTargetData, otherLlvmType)
val compilerGeneratedFile by lazy {
DICreateFile(builder, "<compiler-generated>", "")!!
}
}
/**
* File entry starts offsets from zero while dwarf number lines/column starting from 1.
*/
private val NO_SOURCE_FILE = "no source file"
private fun FileEntry.location(offset: Int, offsetToNumber: (Int) -> Int): Int {
assert(offset != UNDEFINED_OFFSET)
// Part "name.isEmpty() || name == NO_SOURCE_FILE" is an awful hack, @minamoto, please fix properly.
if (offset == SYNTHETIC_OFFSET || name.isEmpty() || name == NO_SOURCE_FILE) return 1
// lldb uses 1-based unsigned integers, so 0 is "no-info".
val result = offsetToNumber(offset) + 1
assert(result != 0)
return result
}
internal fun FileEntry.line(offset: Int) = location(offset, this::getLineNumber)
internal fun FileEntry.column(offset: Int) = location(offset, this::getColumnNumber)
internal data class FileAndFolder(val file: String, val folder: String) {
companion object {
val NOFILE = FileAndFolder("-", "")
}
fun path() = if (this == NOFILE) file else "$folder/$file"
}
internal fun String?.toFileAndFolder(context: Context):FileAndFolder {
this ?: return FileAndFolder.NOFILE
val file = File(this).absoluteFile
var parent = file.parent
context.configuration.get(KonanConfigKeys.DEBUG_PREFIX_MAP)?.let { debugPrefixMap ->
for ((key, value) in debugPrefixMap) {
if (parent.startsWith(key)) {
parent = value + parent.removePrefix(key)
}
}
}
return FileAndFolder(file.name, parent)
}
internal fun generateDebugInfoHeader(context: Context) {
if (context.shouldContainAnyDebugInfo()) {
val path = context.config.outputFile
.toFileAndFolder(context)
@Suppress("UNCHECKED_CAST")
context.debugInfo.module = DICreateModule(
builder = context.debugInfo.builder,
scope = null,
name = path.path(),
configurationMacro = "",
includePath = "",
iSysRoot = "")
/* TODO: figure out what here 2 means:
*
* 0:b-backend-dwarf:minamoto@minamoto-osx(0)# cat /dev/null | clang -xc -S -emit-llvm -g -o - -
* ; ModuleID = '-'
* source_filename = "-"
* target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
* target triple = "x86_64-apple-macosx10.12.0"
*
* !llvm.dbg.cu = !{!0}
* !llvm.module.flags = !{!3, !4, !5}
* !llvm.ident = !{!6}
*
* !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "Apple LLVM version 8.0.0 (clang-800.0.38)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
* !1 = !DIFile(filename: "-", directory: "/Users/minamoto/ws/.git-trees/backend-dwarf")
* !2 = !{}
* !3 = !{i32 2, !"Dwarf Version", i32 2} ; <-
* !4 = !{i32 2, !"Debug Info Version", i32 700000003} ; <-
* !5 = !{i32 1, !"PIC Level", i32 2}
* !6 = !{!"Apple LLVM version 8.0.0 (clang-800.0.38)"}
*/
val llvmTwo = Int32(2).llvm
val dwarfVersion = node(llvmTwo, DWARF.dwarfVersionMetaDataNodeName, Int32(DWARF.dwarfVersion(context.config)).llvm)
val nodeDebugInfoVersion = node(llvmTwo, DWARF.dwarfDebugInfoMetaDataNodeName, Int32(DWARF.debugInfoVersion).llvm)
val llvmModuleFlags = "llvm.module.flags"
LLVMAddNamedMetadataOperand(context.llvmModule, llvmModuleFlags, dwarfVersion)
LLVMAddNamedMetadataOperand(context.llvmModule, llvmModuleFlags, nodeDebugInfoVersion)
val objHeaderType = DICreateStructType(
refBuilder = context.debugInfo.builder,
// TODO: here should be DIFile as scope.
scope = null,
name = "ObjHeader",
file = null,
lineNumber = 0,
sizeInBits = 0,
alignInBits = 0,
flags = DWARF.flagsForwardDeclaration,
derivedFrom = null,
elements = null,
elementsCount = 0,
refPlace = null).cast<DITypeOpaqueRef>()
context.debugInfo.objHeaderPointerType = dwarfPointerType(context, objHeaderType)
}
}
@Suppress("UNCHECKED_CAST")
internal fun IrType.dwarfType(context: Context, targetData: LLVMTargetDataRef): DITypeOpaqueRef {
when {
this.computePrimitiveBinaryTypeOrNull() != null -> return debugInfoBaseType(context, targetData, this.render(), llvmType(context), encoding().value.toInt())
else -> {
return when {
classOrNull != null || this.isTypeParameter() -> context.debugInfo.objHeaderPointerType!!
else -> TODO("$this: Does this case really exist?")
}
}
}
}
internal fun IrType.diType(context: Context, llvmTargetData: LLVMTargetDataRef): DITypeOpaqueRef =
context.debugInfo.types.getOrPut(this) {
dwarfType(context, llvmTargetData)
}
@Suppress("UNCHECKED_CAST")
private fun debugInfoBaseType(context:Context, targetData:LLVMTargetDataRef, typeName:String, type:LLVMTypeRef, encoding:Int) = DICreateBasicType(
context.debugInfo.builder, typeName,
LLVMSizeOfTypeInBits(targetData, type),
LLVMPreferredAlignmentOfType(targetData, type).toLong(), encoding) as DITypeOpaqueRef
internal val IrFunction.types:List<IrType>
get() {
val parameters = valueParameters.map { it.type }
return listOf(returnType, *parameters.toTypedArray())
}
internal fun IrType.size(context:Context) = context.debugInfo.llvmTypeSizes.getOrDefault(this, context.debugInfo.otherTypeSize)
internal fun IrType.alignment(context:Context) = context.debugInfo.llvmTypeAlignments.getOrDefault(this, context.debugInfo.otherTypeAlignment).toLong()
internal fun IrType.llvmType(context:Context): LLVMTypeRef = context.debugInfo.llvmTypes.getOrElse(this) {
when(computePrimitiveBinaryTypeOrNull()) {
PrimitiveBinaryType.BYTE -> context.llvm.llvmInt8
PrimitiveBinaryType.SHORT -> context.llvm.llvmInt16
PrimitiveBinaryType.INT -> context.llvm.llvmInt32
PrimitiveBinaryType.LONG -> context.llvm.llvmInt64
PrimitiveBinaryType.FLOAT -> context.llvm.llvmFloat
PrimitiveBinaryType.DOUBLE -> context.llvm.llvmDouble
PrimitiveBinaryType.VECTOR128 -> context.llvm.llvmVector128
else -> context.debugInfo.otherLlvmType
}
}
internal fun IrType.encoding(): DwarfTypeKind = when(computePrimitiveBinaryTypeOrNull()) {
PrimitiveBinaryType.FLOAT -> DwarfTypeKind.DW_ATE_float
PrimitiveBinaryType.DOUBLE -> DwarfTypeKind.DW_ATE_float
PrimitiveBinaryType.BOOLEAN -> DwarfTypeKind.DW_ATE_boolean
PrimitiveBinaryType.POINTER -> DwarfTypeKind.DW_ATE_address
else -> {
//TODO: not recursive.
if (this.isUnsigned()) DwarfTypeKind.DW_ATE_unsigned
else DwarfTypeKind.DW_ATE_signed
}
}
internal fun alignTo(value:Long, align:Long):Long = (value + align - 1) / align * align
internal fun IrFunction.subroutineType(context: Context, llvmTargetData: LLVMTargetDataRef): DISubroutineTypeRef {
val types = this@subroutineType.types
return subroutineType(context, llvmTargetData, types)
}
internal fun subroutineType(context: Context, llvmTargetData: LLVMTargetDataRef, types: List<IrType>): DISubroutineTypeRef {
return memScoped {
DICreateSubroutineType(context.debugInfo.builder, allocArrayOf(
types.map { it.diType(context, llvmTargetData) }),
types.size)!!
}
}
@Suppress("UNCHECKED_CAST")
private fun dwarfPointerType(context: Context, type: DITypeOpaqueRef) =
DICreatePointerType(context.debugInfo.builder, type) as DITypeOpaqueRef
internal fun setupBridgeDebugInfo(context: Context, function: LLVMValueRef): LocationInfo? {
if (!context.shouldContainLocationDebugInfo()) {
return null
}
val file = context.debugInfo.compilerGeneratedFile
// TODO: can we share the scope among all bridges?
val scope: DIScopeOpaqueRef = DICreateFunction(
builder = context.debugInfo.builder,
scope = file.reinterpret(),
name = function.name,
linkageName = function.name,
file = file,
lineNo = 0,
type = subroutineType(context, context.llvm.runtime.targetData, emptyList()), // TODO: use proper type.
isLocal = 0,
isDefinition = 1,
scopeLine = 0
)!!.also {
DIFunctionAddSubprogram(function, it)
}.reinterpret()
return LocationInfo(scope, 1, 0)
}
@@ -0,0 +1,309 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
/**
* This code was generated with following command:
* $ clang -xc -E -Idist/dependencies/clang-llvm-3.9.0-darwin-macos/include/ llvmDebugInfoC/src/dwarf/include/dwarf_util.kt.pp -Wp,-P -Wp,-CC -o - | sed -e '/^$/d' -e '/^\ *\/\/.*$/d' > backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Dwarf.kt
*
*/
internal enum class DwarfTag(val value:Int) {
DW_TAG_array_type(0x0001),
DW_TAG_class_type(0x0002),
DW_TAG_entry_point(0x0003),
DW_TAG_enumeration_type(0x0004),
DW_TAG_formal_parameter(0x0005),
DW_TAG_imported_declaration(0x0008),
DW_TAG_label(0x000a),
DW_TAG_lexical_block(0x000b),
DW_TAG_member(0x000d),
DW_TAG_pointer_type(0x000f),
DW_TAG_reference_type(0x0010),
DW_TAG_compile_unit(0x0011),
DW_TAG_string_type(0x0012),
DW_TAG_structure_type(0x0013),
DW_TAG_subroutine_type(0x0015),
DW_TAG_typedef(0x0016),
DW_TAG_union_type(0x0017),
DW_TAG_unspecified_parameters(0x0018),
DW_TAG_variant(0x0019),
DW_TAG_common_block(0x001a),
DW_TAG_common_inclusion(0x001b),
DW_TAG_inheritance(0x001c),
DW_TAG_inlined_subroutine(0x001d),
DW_TAG_module(0x001e),
DW_TAG_ptr_to_member_type(0x001f),
DW_TAG_set_type(0x0020),
DW_TAG_subrange_type(0x0021),
DW_TAG_with_stmt(0x0022),
DW_TAG_access_declaration(0x0023),
DW_TAG_base_type(0x0024),
DW_TAG_catch_block(0x0025),
DW_TAG_const_type(0x0026),
DW_TAG_constant(0x0027),
DW_TAG_enumerator(0x0028),
DW_TAG_file_type(0x0029),
DW_TAG_friend(0x002a),
DW_TAG_namelist(0x002b),
DW_TAG_namelist_item(0x002c),
DW_TAG_packed_type(0x002d),
DW_TAG_subprogram(0x002e),
DW_TAG_template_type_parameter(0x002f),
DW_TAG_template_value_parameter(0x0030),
DW_TAG_thrown_type(0x0031),
DW_TAG_try_block(0x0032),
DW_TAG_variant_part(0x0033),
DW_TAG_variable(0x0034),
DW_TAG_volatile_type(0x0035),
DW_TAG_dwarf_procedure(0x0036),
DW_TAG_restrict_type(0x0037),
DW_TAG_interface_type(0x0038),
DW_TAG_namespace(0x0039),
DW_TAG_imported_module(0x003a),
DW_TAG_unspecified_type(0x003b),
DW_TAG_partial_unit(0x003c),
DW_TAG_imported_unit(0x003d),
DW_TAG_condition(0x003f),
DW_TAG_shared_type(0x0040),
DW_TAG_type_unit(0x0041),
DW_TAG_rvalue_reference_type(0x0042),
DW_TAG_template_alias(0x0043),
DW_TAG_coarray_type(0x0044),
DW_TAG_generic_subrange(0x0045),
DW_TAG_dynamic_type(0x0046),
DW_TAG_MIPS_loop(0x4081),
DW_TAG_format_label(0x4101),
DW_TAG_function_template(0x4102),
DW_TAG_class_template(0x4103),
DW_TAG_GNU_template_template_param(0x4106),
DW_TAG_GNU_template_parameter_pack(0x4107),
DW_TAG_GNU_formal_parameter_pack(0x4108),
DW_TAG_APPLE_property(0x4200),
DW_TAG_BORLAND_property(0xb000),
DW_TAG_BORLAND_Delphi_string(0xb001),
DW_TAG_BORLAND_Delphi_dynamic_array(0xb002),
DW_TAG_BORLAND_Delphi_set(0xb003),
DW_TAG_BORLAND_Delphi_variant(0xb004),
}
internal enum class DwarfTypeKind(val value:Byte) {
DW_ATE_address(0x01),
DW_ATE_boolean(0x02),
DW_ATE_complex_float(0x03),
DW_ATE_float(0x04),
DW_ATE_signed(0x05),
DW_ATE_signed_char(0x06),
DW_ATE_unsigned(0x07),
DW_ATE_unsigned_char(0x08),
DW_ATE_imaginary_float(0x09),
DW_ATE_packed_decimal(0x0a),
DW_ATE_numeric_string(0x0b),
DW_ATE_edited(0x0c),
DW_ATE_signed_fixed(0x0d),
DW_ATE_unsigned_fixed(0x0e),
DW_ATE_decimal_float(0x0f),
DW_ATE_UTF(0x10),
}
internal enum class DwarfOp(val value:Long) {
DW_OP_addr(0x03),
DW_OP_deref(0x06),
DW_OP_const1u(0x08),
DW_OP_const1s(0x09),
DW_OP_const2u(0x0a),
DW_OP_const2s(0x0b),
DW_OP_const4u(0x0c),
DW_OP_const4s(0x0d),
DW_OP_const8u(0x0e),
DW_OP_const8s(0x0f),
DW_OP_constu(0x10),
DW_OP_consts(0x11),
DW_OP_dup(0x12),
DW_OP_drop(0x13),
DW_OP_over(0x14),
DW_OP_pick(0x15),
DW_OP_swap(0x16),
DW_OP_rot(0x17),
DW_OP_xderef(0x18),
DW_OP_abs(0x19),
DW_OP_and(0x1a),
DW_OP_div(0x1b),
DW_OP_minus(0x1c),
DW_OP_mod(0x1d),
DW_OP_mul(0x1e),
DW_OP_neg(0x1f),
DW_OP_not(0x20),
DW_OP_or(0x21),
DW_OP_plus(0x22),
DW_OP_plus_uconst(0x23),
DW_OP_shl(0x24),
DW_OP_shr(0x25),
DW_OP_shra(0x26),
DW_OP_xor(0x27),
DW_OP_skip(0x2f),
DW_OP_bra(0x28),
DW_OP_eq(0x29),
DW_OP_ge(0x2a),
DW_OP_gt(0x2b),
DW_OP_le(0x2c),
DW_OP_lt(0x2d),
DW_OP_ne(0x2e),
DW_OP_lit0(0x30),
DW_OP_lit1(0x31),
DW_OP_lit2(0x32),
DW_OP_lit3(0x33),
DW_OP_lit4(0x34),
DW_OP_lit5(0x35),
DW_OP_lit6(0x36),
DW_OP_lit7(0x37),
DW_OP_lit8(0x38),
DW_OP_lit9(0x39),
DW_OP_lit10(0x3a),
DW_OP_lit11(0x3b),
DW_OP_lit12(0x3c),
DW_OP_lit13(0x3d),
DW_OP_lit14(0x3e),
DW_OP_lit15(0x3f),
DW_OP_lit16(0x40),
DW_OP_lit17(0x41),
DW_OP_lit18(0x42),
DW_OP_lit19(0x43),
DW_OP_lit20(0x44),
DW_OP_lit21(0x45),
DW_OP_lit22(0x46),
DW_OP_lit23(0x47),
DW_OP_lit24(0x48),
DW_OP_lit25(0x49),
DW_OP_lit26(0x4a),
DW_OP_lit27(0x4b),
DW_OP_lit28(0x4c),
DW_OP_lit29(0x4d),
DW_OP_lit30(0x4e),
DW_OP_lit31(0x4f),
DW_OP_reg0(0x50),
DW_OP_reg1(0x51),
DW_OP_reg2(0x52),
DW_OP_reg3(0x53),
DW_OP_reg4(0x54),
DW_OP_reg5(0x55),
DW_OP_reg6(0x56),
DW_OP_reg7(0x57),
DW_OP_reg8(0x58),
DW_OP_reg9(0x59),
DW_OP_reg10(0x5a),
DW_OP_reg11(0x5b),
DW_OP_reg12(0x5c),
DW_OP_reg13(0x5d),
DW_OP_reg14(0x5e),
DW_OP_reg15(0x5f),
DW_OP_reg16(0x60),
DW_OP_reg17(0x61),
DW_OP_reg18(0x62),
DW_OP_reg19(0x63),
DW_OP_reg20(0x64),
DW_OP_reg21(0x65),
DW_OP_reg22(0x66),
DW_OP_reg23(0x67),
DW_OP_reg24(0x68),
DW_OP_reg25(0x69),
DW_OP_reg26(0x6a),
DW_OP_reg27(0x6b),
DW_OP_reg28(0x6c),
DW_OP_reg29(0x6d),
DW_OP_reg30(0x6e),
DW_OP_reg31(0x6f),
DW_OP_breg0(0x70),
DW_OP_breg1(0x71),
DW_OP_breg2(0x72),
DW_OP_breg3(0x73),
DW_OP_breg4(0x74),
DW_OP_breg5(0x75),
DW_OP_breg6(0x76),
DW_OP_breg7(0x77),
DW_OP_breg8(0x78),
DW_OP_breg9(0x79),
DW_OP_breg10(0x7a),
DW_OP_breg11(0x7b),
DW_OP_breg12(0x7c),
DW_OP_breg13(0x7d),
DW_OP_breg14(0x7e),
DW_OP_breg15(0x7f),
DW_OP_breg16(0x80),
DW_OP_breg17(0x81),
DW_OP_breg18(0x82),
DW_OP_breg19(0x83),
DW_OP_breg20(0x84),
DW_OP_breg21(0x85),
DW_OP_breg22(0x86),
DW_OP_breg23(0x87),
DW_OP_breg24(0x88),
DW_OP_breg25(0x89),
DW_OP_breg26(0x8a),
DW_OP_breg27(0x8b),
DW_OP_breg28(0x8c),
DW_OP_breg29(0x8d),
DW_OP_breg30(0x8e),
DW_OP_breg31(0x8f),
DW_OP_regx(0x90),
DW_OP_fbreg(0x91),
DW_OP_bregx(0x92),
DW_OP_piece(0x93),
DW_OP_deref_size(0x94),
DW_OP_xderef_size(0x95),
DW_OP_nop(0x96),
DW_OP_push_object_address(0x97),
DW_OP_call2(0x98),
DW_OP_call4(0x99),
DW_OP_call_ref(0x9a),
DW_OP_form_tls_address(0x9b),
DW_OP_call_frame_cfa(0x9c),
DW_OP_bit_piece(0x9d),
DW_OP_implicit_value(0x9e),
DW_OP_stack_value(0x9f),
DW_OP_GNU_push_tls_address(0xe0),
DW_OP_GNU_addr_index(0xfb),
DW_OP_GNU_const_index(0xfc),
}
internal enum class DwarfLanguage(val value:Int) {
DW_LANG_C89(0x0001),
DW_LANG_C(0x0002),
DW_LANG_Ada83(0x0003),
DW_LANG_C_plus_plus(0x0004),
DW_LANG_Cobol74(0x0005),
DW_LANG_Cobol85(0x0006),
DW_LANG_Fortran77(0x0007),
DW_LANG_Fortran90(0x0008),
DW_LANG_Pascal83(0x0009),
DW_LANG_Modula2(0x000a),
DW_LANG_Java(0x000b),
DW_LANG_C99(0x000c),
DW_LANG_Ada95(0x000d),
DW_LANG_Fortran95(0x000e),
DW_LANG_PLI(0x000f),
DW_LANG_ObjC(0x0010),
DW_LANG_ObjC_plus_plus(0x0011),
DW_LANG_UPC(0x0012),
DW_LANG_D(0x0013),
DW_LANG_Python(0x0014),
DW_LANG_OpenCL(0x0015),
DW_LANG_Go(0x0016),
DW_LANG_Modula3(0x0017),
DW_LANG_Haskell(0x0018),
DW_LANG_C_plus_plus_03(0x0019),
DW_LANG_C_plus_plus_11(0x001a),
DW_LANG_OCaml(0x001b),
DW_LANG_Rust(0x001c),
DW_LANG_C11(0x001d),
DW_LANG_Swift(0x001e),
DW_LANG_Julia(0x001f),
DW_LANG_Dylan(0x0020),
DW_LANG_C_plus_plus_14(0x0021),
DW_LANG_Fortran03(0x0022),
DW_LANG_Fortran08(0x0023),
DW_LANG_Mips_Assembler(0x8001),
DW_LANG_GOOGLE_RenderScript(0x8e57),
DW_LANG_BORLAND_Delphi(0xb000),
DW_LANG_Kotlin(0x0026)
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
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
internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
val config = context.config.configuration
if (config.get(KonanConfigKeys.PRODUCE) != PROGRAM) return null
val entryPoint = FqName(config.get(KonanConfigKeys.ENTRY) ?: defaultEntryName(config))
val entryName = entryPoint.shortName()
val packageName = entryPoint.parent()
val packageScope = context.builtIns.builtInsModule.getPackage(packageName).memberScope
val candidates = packageScope.getContributedFunctions(entryName,
NoLookupLocation.FROM_BACKEND).filter {
it.returnType?.isUnit() == true &&
it.typeParameters.isEmpty() &&
it.visibility.isPublicAPI
}
val main =
candidates.singleOrNull { it.hasSingleArrayOfStringParameter } ?:
candidates.singleOrNull { it.hasNoParameters } ?:
context.reportCompilationError("Could not find '$entryName' in '$packageName' package.")
if (main.isSuspend)
context.reportCompilationError("Entry point can not be a suspend function.")
return main
}
private fun defaultEntryName(config: CompilerConfiguration): String =
when (config.get(KonanConfigKeys.GENERATE_TEST_RUNNER)) {
TestRunnerKind.MAIN_THREAD -> "kotlin.native.internal.test.main"
TestRunnerKind.WORKER -> "kotlin.native.internal.test.worker"
TestRunnerKind.MAIN_THREAD_NO_EXIT -> "kotlin.native.internal.test.mainNoExit"
else -> "main"
}
private val KotlinType.filterClass: ClassDescriptor?
get() {
val constr = constructor.declarationDescriptor
return constr as? ClassDescriptor
}
private val ClassDescriptor.isString
get() = fqNameSafe.asString() == "kotlin.String"
private val KotlinType.isString
get() = filterClass?.isString ?: false
private val KotlinType.isArrayOfString: Boolean
get() = (filterClass?.isArray ?: false) &&
(arguments.singleOrNull()?.type?.isString ?: false)
private val FunctionDescriptor.hasSingleArrayOfStringParameter: Boolean
get() = valueParameters.singleOrNull()?.type?.isArrayOfString ?: false
private val FunctionDescriptor.hasNoParameters: Boolean
get() = valueParameters.isEmpty()
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import org.jetbrains.kotlin.backend.konan.hash.*
internal fun localHash(data: ByteArray): Long {
memScoped {
val res = alloc<LocalHashVar>()
val bytes = allocArrayOf(data)
MakeLocalHash(bytes, data.size, res.ptr)
return res.value
}
}
internal fun globalHash(data: ByteArray, retValPlacement: NativePlacement): GlobalHash {
val res = retValPlacement.alloc<GlobalHash>()
memScoped {
val bytes = allocArrayOf(data)
MakeGlobalHash(bytes, data.size, res.ptr)
}
return res
}
public fun base64Encode(data: ByteArray): String {
memScoped {
val resultSize = 4 * data.size / 3 + 3 + 1
val result = allocArray<ByteVar>(resultSize)
val bytes = allocArrayOf(data)
EncodeBase64(bytes, data.size, result, resultSize)
// TODO: any better way to do that without two copies?
return result.toKString()
}
}
public fun base64Decode(encoded: String): ByteArray {
memScoped {
val bufferSize: Int = 3 * encoded.length / 4
val result = allocArray<ByteVar>(bufferSize)
val resultSize = allocArray<uint32_tVar>(1)
resultSize[0] = bufferSize
val errorCode = DecodeBase64(encoded, encoded.length, result, resultSize)
if (errorCode != 0) throw Error("Non-zero exit code of DecodeBase64: ${errorCode}")
val realSize = resultSize[0]
return result.readBytes(realSize)
}
}
internal class LocalHash(val value: Long) : ConstValue by Int64(value)
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin
import org.jetbrains.kotlin.descriptors.konan.klibModuleOrigin
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.resolve.descriptorUtil.module
internal interface LlvmImports {
fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean = false)
fun bitcodeIsUsed(library: KonanLibrary): Boolean
fun nativeDependenciesAreUsed(library: KonanLibrary): Boolean
}
internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKlibModuleOrigin
get() {
assert(!this.isExpectMember) { this }
val module = this.module
val moduleOrigin = module.klibModuleOrigin
when (moduleOrigin) {
is CompiledKlibModuleOrigin -> return moduleOrigin
SyntheticModulesOrigin -> error("Declaration is synthetic and can't be an origin of LLVM symbol:\n${this}")
}
}
internal val Context.standardLlvmSymbolsOrigin: CompiledKlibModuleOrigin get() = this.stdlibModule.llvmSymbolOrigin
@@ -0,0 +1,725 @@
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.cValuesOf
import llvm.*
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
import org.jetbrains.kotlin.backend.konan.llvm.objc.genObjCSelector
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.ir.util.isSuspend
internal enum class IntrinsicType {
PLUS,
MINUS,
TIMES,
SIGNED_DIV,
SIGNED_REM,
UNSIGNED_DIV,
UNSIGNED_REM,
INC,
DEC,
UNARY_PLUS,
UNARY_MINUS,
SHL,
SHR,
USHR,
AND,
OR,
XOR,
INV,
SIGN_EXTEND,
ZERO_EXTEND,
INT_TRUNCATE,
FLOAT_TRUNCATE,
FLOAT_EXTEND,
SIGNED_TO_FLOAT,
UNSIGNED_TO_FLOAT,
FLOAT_TO_SIGNED,
SIGNED_COMPARE_TO,
UNSIGNED_COMPARE_TO,
NOT,
REINTERPRET,
EXTRACT_ELEMENT,
ARE_EQUAL_BY_VALUE,
IEEE_754_EQUALS,
// OBJC
OBJC_GET_MESSENGER,
OBJC_GET_MESSENGER_STRET,
OBJC_GET_OBJC_CLASS,
OBJC_CREATE_SUPER_STRUCT,
OBJC_INIT_BY,
OBJC_GET_SELECTOR,
// Other
GET_CLASS_TYPE_INFO,
CREATE_UNINITIALIZED_INSTANCE,
LIST_OF_INTERNAL,
IDENTITY,
IMMUTABLE_BLOB,
INIT_INSTANCE,
// Enums
ENUM_VALUES,
ENUM_VALUE_OF,
// Coroutines
GET_CONTINUATION,
RETURN_IF_SUSPENDED,
COROUTINE_LAUNCHPAD,
// Interop
INTEROP_READ_BITS,
INTEROP_WRITE_BITS,
INTEROP_READ_PRIMITIVE,
INTEROP_WRITE_PRIMITIVE,
INTEROP_GET_POINTER_SIZE,
INTEROP_NATIVE_PTR_TO_LONG,
INTEROP_NATIVE_PTR_PLUS_LONG,
INTEROP_GET_NATIVE_NULL_PTR,
INTEROP_CONVERT,
INTEROP_BITS_TO_FLOAT,
INTEROP_BITS_TO_DOUBLE,
INTEROP_SIGN_EXTEND,
INTEROP_NARROW,
INTEROP_STATIC_C_FUNCTION,
INTEROP_FUNPTR_INVOKE,
INTEROP_MEMORY_COPY,
// Worker
WORKER_EXECUTE
}
// Explicit and single interface between Intrinsic Generator and IrToBitcode.
internal interface IntrinsicGeneratorEnvironment {
val codegen: CodeGenerator
val functionGenerationContext: FunctionGenerationContext
val continuation: LLVMValueRef
val exceptionHandler: ExceptionHandler
val stackLocalsManager: StackLocalsManager
fun calculateLifetime(element: IrElement): Lifetime
fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass? = null): LLVMValueRef
fun evaluateExplicitArgs(expression: IrFunctionAccessExpression): List<LLVMValueRef>
fun evaluateExpression(value: IrExpression): LLVMValueRef
}
internal fun tryGetIntrinsicType(callSite: IrFunctionAccessExpression): IntrinsicType? =
if (callSite.symbol.owner.isTypedIntrinsic) getIntrinsicType(callSite) else null
private fun getIntrinsicType(callSite: IrFunctionAccessExpression): IntrinsicType {
val function = callSite.symbol.owner
val annotation = function.annotations.findAnnotation(RuntimeNames.typedIntrinsicAnnotation)!!
val value = annotation.getAnnotationStringValue()!!
return IntrinsicType.valueOf(value)
}
internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnvironment) {
private val codegen = environment.codegen
private val context = codegen.context
private val IrCall.llvmReturnType: LLVMTypeRef
get() = LLVMGetReturnType(codegen.getLlvmFunctionType(symbol.owner))!!
private fun LLVMTypeRef.sizeInBits() = LLVMSizeOfTypeInBits(codegen.llvmTargetData, this).toInt()
/**
* Some intrinsics have to be processed before evaluation of their arguments.
* So this method looks at [callSite] and if it is call to "special" intrinsic
* processes it. Otherwise it returns null.
*/
fun tryEvaluateSpecialCall(callSite: IrFunctionAccessExpression): LLVMValueRef? {
val function = callSite.symbol.owner
if (!function.isTypedIntrinsic) {
return null
}
return when (getIntrinsicType(callSite)) {
IntrinsicType.IMMUTABLE_BLOB -> {
@Suppress("UNCHECKED_CAST")
val arg = callSite.getValueArgument(0) as IrConst<String>
context.llvm.staticData.createImmutableBlob(arg)
}
IntrinsicType.OBJC_GET_SELECTOR -> {
val selector = (callSite.getValueArgument(0) as IrConst<*>).value as String
environment.functionGenerationContext.genObjCSelector(selector)
}
IntrinsicType.INIT_INSTANCE -> {
val initializer = callSite.getValueArgument(1) as IrConstructorCall
val thiz = environment.evaluateExpression(callSite.getValueArgument(0)!!)
environment.evaluateCall(
initializer.symbol.owner,
listOf(thiz) + environment.evaluateExplicitArgs(initializer),
environment.calculateLifetime(initializer)
)
codegen.theUnitInstanceRef.llvm
}
IntrinsicType.COROUTINE_LAUNCHPAD -> {
val suspendFunctionCall = callSite.getValueArgument(0) as IrCall
val continuation = environment.evaluateExpression(callSite.getValueArgument(1)!!)
val suspendFunction = suspendFunctionCall.symbol.owner
assert(suspendFunction.isSuspend) { "Call to a suspend function expected but was ${suspendFunction.dump()}" }
environment.evaluateCall(suspendFunction,
environment.evaluateExplicitArgs(suspendFunctionCall) + listOf(continuation),
environment.calculateLifetime(suspendFunctionCall),
suspendFunction.parent as? IrClass // Call non-virtually.
)
}
else -> null
}
}
fun evaluateCall(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef =
environment.functionGenerationContext.evaluateCall(callSite, args)
// Assuming that we checked for `TypedIntrinsic` annotation presence.
private fun FunctionGenerationContext.evaluateCall(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef =
when (val intrinsicType = getIntrinsicType(callSite)) {
IntrinsicType.PLUS -> emitPlus(args)
IntrinsicType.MINUS -> emitMinus(args)
IntrinsicType.TIMES -> emitTimes(args)
IntrinsicType.SIGNED_DIV -> emitSignedDiv(args)
IntrinsicType.SIGNED_REM -> emitSignedRem(args)
IntrinsicType.UNSIGNED_DIV -> emitUnsignedDiv(args)
IntrinsicType.UNSIGNED_REM -> emitUnsignedRem(args)
IntrinsicType.INC -> emitInc(args)
IntrinsicType.DEC -> emitDec(args)
IntrinsicType.UNARY_PLUS -> emitUnaryPlus(args)
IntrinsicType.UNARY_MINUS -> emitUnaryMinus(args)
IntrinsicType.SHL -> emitShl(args)
IntrinsicType.SHR -> emitShr(args)
IntrinsicType.USHR -> emitUshr(args)
IntrinsicType.AND -> emitAnd(args)
IntrinsicType.OR -> emitOr(args)
IntrinsicType.XOR -> emitXor(args)
IntrinsicType.INV -> emitInv(args)
IntrinsicType.SIGNED_COMPARE_TO -> emitSignedCompareTo(args)
IntrinsicType.UNSIGNED_COMPARE_TO -> emitUnsignedCompareTo(args)
IntrinsicType.NOT -> emitNot(args)
IntrinsicType.REINTERPRET -> emitReinterpret(callSite, args)
IntrinsicType.EXTRACT_ELEMENT -> emitExtractElement(callSite, args)
IntrinsicType.SIGN_EXTEND -> emitSignExtend(callSite, args)
IntrinsicType.ZERO_EXTEND -> emitZeroExtend(callSite, args)
IntrinsicType.INT_TRUNCATE -> emitIntTruncate(callSite, args)
IntrinsicType.SIGNED_TO_FLOAT -> emitSignedToFloat(callSite, args)
IntrinsicType.UNSIGNED_TO_FLOAT -> emitUnsignedToFloat(callSite, args)
IntrinsicType.FLOAT_TO_SIGNED -> emitFloatToSigned(callSite, args)
IntrinsicType.FLOAT_EXTEND -> emitFloatExtend(callSite, args)
IntrinsicType.FLOAT_TRUNCATE -> emitFloatTruncate(callSite, args)
IntrinsicType.ARE_EQUAL_BY_VALUE -> emitAreEqualByValue(args)
IntrinsicType.IEEE_754_EQUALS -> emitIeee754Equals(args)
IntrinsicType.OBJC_GET_MESSENGER -> emitObjCGetMessenger(args, isStret = false)
IntrinsicType.OBJC_GET_MESSENGER_STRET -> emitObjCGetMessenger(args, isStret = true)
IntrinsicType.OBJC_GET_OBJC_CLASS -> emitGetObjCClass(callSite)
IntrinsicType.OBJC_CREATE_SUPER_STRUCT -> emitObjCCreateSuperStruct(args)
IntrinsicType.GET_CLASS_TYPE_INFO -> emitGetClassTypeInfo(callSite)
IntrinsicType.INTEROP_READ_BITS -> emitReadBits(args)
IntrinsicType.INTEROP_WRITE_BITS -> emitWriteBits(args)
IntrinsicType.INTEROP_READ_PRIMITIVE -> emitReadPrimitive(callSite, args)
IntrinsicType.INTEROP_WRITE_PRIMITIVE -> emitWritePrimitive(callSite, args)
IntrinsicType.INTEROP_GET_POINTER_SIZE -> emitGetPointerSize()
IntrinsicType.CREATE_UNINITIALIZED_INSTANCE -> emitCreateUninitializedInstance(callSite)
IntrinsicType.INTEROP_NATIVE_PTR_TO_LONG -> emitNativePtrToLong(callSite, args)
IntrinsicType.INTEROP_NATIVE_PTR_PLUS_LONG -> emitNativePtrPlusLong(args)
IntrinsicType.INTEROP_GET_NATIVE_NULL_PTR -> emitGetNativeNullPtr()
IntrinsicType.LIST_OF_INTERNAL -> emitListOfInternal(callSite, args)
IntrinsicType.IDENTITY -> emitIdentity(args)
IntrinsicType.GET_CONTINUATION -> emitGetContinuation()
IntrinsicType.INTEROP_MEMORY_COPY -> emitMemoryCopy(callSite, args)
IntrinsicType.RETURN_IF_SUSPENDED,
IntrinsicType.INTEROP_BITS_TO_FLOAT,
IntrinsicType.INTEROP_BITS_TO_DOUBLE,
IntrinsicType.INTEROP_SIGN_EXTEND,
IntrinsicType.INTEROP_NARROW,
IntrinsicType.INTEROP_STATIC_C_FUNCTION,
IntrinsicType.INTEROP_FUNPTR_INVOKE,
IntrinsicType.INTEROP_CONVERT,
IntrinsicType.ENUM_VALUES,
IntrinsicType.ENUM_VALUE_OF,
IntrinsicType.WORKER_EXECUTE ->
reportNonLoweredIntrinsic(intrinsicType)
IntrinsicType.INIT_INSTANCE,
IntrinsicType.OBJC_INIT_BY,
IntrinsicType.COROUTINE_LAUNCHPAD,
IntrinsicType.OBJC_GET_SELECTOR,
IntrinsicType.IMMUTABLE_BLOB ->
reportSpecialIntrinsic(intrinsicType)
}
private fun reportSpecialIntrinsic(intrinsicType: IntrinsicType): Nothing =
context.reportCompilationError("$intrinsicType should be handled by `tryEvaluateSpecialCall`")
private fun reportNonLoweredIntrinsic(intrinsicType: IntrinsicType): Nothing =
context.reportCompilationError("Intrinsic of type $intrinsicType should be handled by previos lowering phase")
private fun FunctionGenerationContext.emitGetContinuation(): LLVMValueRef =
environment.continuation
private fun FunctionGenerationContext.emitIdentity(args: List<LLVMValueRef>): LLVMValueRef =
args.single()
private fun FunctionGenerationContext.emitListOfInternal(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
val varargExpression = callSite.getValueArgument(0) as IrVararg
val vararg = args.single()
val length = varargExpression.elements.size
// TODO: store length in `vararg` itself when more abstract types will be used for values.
val array = constPointer(vararg)
// Note: dirty hack here: `vararg` has type `Array<out E>`, but `createConstArrayList` expects `Array<E>`;
// however `vararg` is immutable, and in current implementation it has type `Array<E>`,
// so let's ignore this mismatch currently for simplicity.
return context.llvm.staticData.createConstArrayList(array, length).llvm
}
private fun FunctionGenerationContext.emitGetNativeNullPtr(): LLVMValueRef =
kNullInt8Ptr
private fun FunctionGenerationContext.emitNativePtrPlusLong(args: List<LLVMValueRef>): LLVMValueRef =
gep(args[0], args[1])
private fun FunctionGenerationContext.emitNativePtrToLong(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
val intPtrValue = ptrToInt(args.single(), codegen.intPtrType)
val resultType = callSite.llvmReturnType
return if (resultType == intPtrValue.type) {
intPtrValue
} else {
LLVMBuildSExt(builder, intPtrValue, resultType, "")!!
}
}
private fun FunctionGenerationContext.emitCreateUninitializedInstance(callSite: IrCall): LLVMValueRef {
val typeParameterT = context.ir.symbols.createUninitializedInstance.descriptor.typeParameters[0]
val enumClass = callSite.getTypeArgument(typeParameterT)!!
val enumIrClass = enumClass.getClass()!!
return allocInstance(enumIrClass, environment.calculateLifetime(callSite), environment.stackLocalsManager)
}
private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef =
Int32(LLVMPointerSize(codegen.llvmTargetData)).llvm
private fun FunctionGenerationContext.emitReadPrimitive(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
val pointerType = pointerType(callSite.llvmReturnType)
val rawPointer = args.last()
val pointer = bitcast(pointerType, rawPointer)
return load(pointer)
}
private fun FunctionGenerationContext.emitWritePrimitive(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
val function = callSite.symbol.owner
val pointerType = pointerType(codegen.getLLVMType(function.valueParameters.last().type))
val rawPointer = args[1]
val pointer = bitcast(pointerType, rawPointer)
store(args[2], pointer)
return codegen.theUnitInstanceRef.llvm
}
private fun FunctionGenerationContext.emitReadBits(args: List<LLVMValueRef>): LLVMValueRef {
val ptr = args[0]
assert(ptr.type == int8TypePtr)
val offset = extractConstUnsignedInt(args[1])
val size = extractConstUnsignedInt(args[2]).toInt()
val signed = extractConstUnsignedInt(args[3]) != 0L
val prefixBitsNum = (offset % 8).toInt()
val suffixBitsNum = (8 - ((size + offset) % 8).toInt()) % 8
// Note: LLVM allows to read without padding tail up to byte boundary, but the result seems to be incorrect.
val bitsWithPaddingNum = prefixBitsNum + size + suffixBitsNum
val bitsWithPaddingType = LLVMIntTypeInContext(llvmContext, bitsWithPaddingNum)!!
val bitsWithPaddingPtr = bitcast(org.jetbrains.kotlin.backend.konan.llvm.pointerType(bitsWithPaddingType), gep(ptr, org.jetbrains.kotlin.backend.konan.llvm.Int64(offset / 8).llvm))
val bitsWithPadding = load(bitsWithPaddingPtr).setUnaligned()
val bits = shr(
shl(bitsWithPadding, suffixBitsNum),
prefixBitsNum + suffixBitsNum, signed
)
return when {
bitsWithPaddingNum == 64 -> bits
bitsWithPaddingNum > 64 -> trunc(bits, org.jetbrains.kotlin.backend.konan.llvm.int64Type)
else -> ext(bits, org.jetbrains.kotlin.backend.konan.llvm.int64Type, signed)
}
}
private fun FunctionGenerationContext.emitWriteBits(args: List<LLVMValueRef>): LLVMValueRef {
val ptr = args[0]
assert(ptr.type == int8TypePtr)
val offset = extractConstUnsignedInt(args[1])
val size = extractConstUnsignedInt(args[2]).toInt()
val value = args[3]
assert(value.type == int64Type)
val bitsType = LLVMIntTypeInContext(llvmContext, size)!!
val prefixBitsNum = (offset % 8).toInt()
val suffixBitsNum = (8 - ((size + offset) % 8).toInt()) % 8
val bitsWithPaddingNum = prefixBitsNum + size + suffixBitsNum
val bitsWithPaddingType = LLVMIntTypeInContext(llvmContext, bitsWithPaddingNum)!!
// 0011111000:
val discardBitsMask = LLVMConstShl(
LLVMConstZExt(
LLVMConstAllOnes(bitsType), // 11111
bitsWithPaddingType
), // 1111100000
LLVMConstInt(bitsWithPaddingType, prefixBitsNum.toLong(), 0)
)
val preservedBitsMask = LLVMConstNot(discardBitsMask)!!
val bitsWithPaddingPtr = bitcast(pointerType(bitsWithPaddingType), gep(ptr, Int64(offset / 8).llvm))
val bits = trunc(value, bitsType)
val bitsToStore = if (prefixBitsNum == 0 && suffixBitsNum == 0) {
bits
} else {
val previousValue = load(bitsWithPaddingPtr).setUnaligned()
val preservedBits = and(previousValue, preservedBitsMask)
val bitsWithPadding = shl(zext(bits, bitsWithPaddingType), prefixBitsNum)
or(bitsWithPadding, preservedBits)
}
llvm.LLVMBuildStore(builder, bitsToStore, bitsWithPaddingPtr)!!.setUnaligned()
return codegen.theUnitInstanceRef.llvm
}
private fun FunctionGenerationContext.emitMemoryCopy(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
println("memcpy at ${callSite}")
args.map { println(llvm2string(it)) }
TODO("Implement me")
}
private fun FunctionGenerationContext.emitGetClassTypeInfo(callSite: IrCall): LLVMValueRef {
val typeArgument = callSite.getTypeArgument(0)!!
val typeArgumentClass = typeArgument.getClass()
return if (typeArgumentClass == null) {
// Should not happen anymore, but it is safer to handle this case.
unreachable()
kNullInt8Ptr
} else {
val typeInfo = codegen.typeInfoValue(typeArgumentClass)
LLVMConstBitCast(typeInfo, kInt8Ptr)!!
}
}
private fun FunctionGenerationContext.emitObjCCreateSuperStruct(args: List<LLVMValueRef>): LLVMValueRef {
assert(args.size == 2)
val receiver = args[0]
val superClass = args[1]
val structType = structType(kInt8Ptr, kInt8Ptr)
val ptr = alloca(structType)
store(receiver, LLVMBuildGEP(builder, ptr, cValuesOf(kImmZero, kImmZero), 2, "")!!)
store(superClass, LLVMBuildGEP(builder, ptr, cValuesOf(kImmZero, kImmOne), 2, "")!!)
return bitcast(int8TypePtr, ptr)
}
// TODO: Find better place for these guys.
private val kImmZero = LLVMConstInt(int32Type, 0, 1)!!
private val kImmOne = LLVMConstInt(int32Type, 1, 1)!!
private fun FunctionGenerationContext.emitGetObjCClass(callSite: IrCall): LLVMValueRef {
val typeArgument = callSite.getTypeArgument(0)
return getObjCClass(typeArgument!!.getClass()!!, environment.exceptionHandler)
}
private fun FunctionGenerationContext.emitObjCGetMessenger(args: List<LLVMValueRef>, isStret: Boolean): LLVMValueRef {
val messengerNameSuffix = if (isStret) "_stret" else ""
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
val libobjc = context.standardLlvmSymbolsOrigin
val normalMessenger = context.llvm.externalFunction(
"objc_msgSend$messengerNameSuffix",
functionType,
origin = libobjc
)
val superMessenger = context.llvm.externalFunction(
"objc_msgSendSuper$messengerNameSuffix",
functionType,
origin = libobjc
)
val superClass = args.single()
val messenger = LLVMBuildSelect(builder,
If = icmpEq(superClass, kNullInt8Ptr),
Then = normalMessenger,
Else = superMessenger,
Name = ""
)!!
return bitcast(int8TypePtr, messenger)
}
private fun FunctionGenerationContext.emitAreEqualByValue(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
assert (first.type == second.type) { "Types are different: '${llvmtype2string(first.type)}' and '${llvmtype2string(second.type)}'" }
return when (val typeKind = LLVMGetTypeKind(first.type)) {
llvm.LLVMTypeKind.LLVMFloatTypeKind, llvm.LLVMTypeKind.LLVMDoubleTypeKind,
LLVMTypeKind.LLVMVectorTypeKind -> {
// TODO LLVM API does not provide guarantee for LLVMIntTypeInContext availability for longer types; consider meaningful diag message instead of NPE
val integerType = LLVMIntTypeInContext(llvmContext, first.type.sizeInBits())!!
icmpEq(bitcast(integerType, first), bitcast(integerType, second))
}
llvm.LLVMTypeKind.LLVMIntegerTypeKind, llvm.LLVMTypeKind.LLVMPointerTypeKind -> icmpEq(first, second)
else -> error(typeKind)
}
}
private fun FunctionGenerationContext.emitIeee754Equals(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
assert (first.type == second.type)
{ "Types are different: '${llvmtype2string(first.type)}' and '${llvmtype2string(second.type)}'" }
val type = LLVMGetTypeKind(first.type)
assert (type == LLVMTypeKind.LLVMFloatTypeKind || type == LLVMTypeKind.LLVMDoubleTypeKind)
{ "Should be of floating point kind, not: '${llvmtype2string(first.type)}'"}
return fcmpEq(first, second)
}
private fun FunctionGenerationContext.emitReinterpret(callSite: IrCall, args: List<LLVMValueRef>) =
bitcast(callSite.llvmReturnType, args[0])
private fun FunctionGenerationContext.emitExtractElement(callSite: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
val (vector, index) = args
val elementSize = LLVMSizeOfTypeInBits(codegen.llvmTargetData, callSite.llvmReturnType).toInt()
val vectorSize = LLVMSizeOfTypeInBits(codegen.llvmTargetData, vector.type).toInt()
assert(callSite.llvmReturnType.isVectorElementType()
&& vectorSize % elementSize == 0
) { "Invalid vector element type ${LLVMGetTypeKind(callSite.llvmReturnType)}"}
val elementCount = vectorSize / elementSize
emitThrowIfOOB(index, Int32((elementCount)).llvm)
val targetType = LLVMVectorType(callSite.llvmReturnType, elementCount)!!
return extractElement(
(if (targetType == vector.type) vector else bitcast(targetType, vector)),
index)
}
private fun FunctionGenerationContext.emitNot(args: List<LLVMValueRef>) =
not(args[0])
private fun FunctionGenerationContext.emitPlus(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return if (first.type.isFloatingPoint()) {
fadd(first, second)
} else {
add(first, second)
}
}
private fun FunctionGenerationContext.emitSignExtend(callSite: IrCall, args: List<LLVMValueRef>) =
sext(args[0], callSite.llvmReturnType)
private fun FunctionGenerationContext.emitZeroExtend(callSite: IrCall, args: List<LLVMValueRef>) =
zext(args[0], callSite.llvmReturnType)
private fun FunctionGenerationContext.emitIntTruncate(callSite: IrCall, args: List<LLVMValueRef>) =
trunc(args[0], callSite.llvmReturnType)
private fun FunctionGenerationContext.emitSignedToFloat(callSite: IrCall, args: List<LLVMValueRef>) =
LLVMBuildSIToFP(builder, args[0], callSite.llvmReturnType, "")!!
private fun FunctionGenerationContext.emitUnsignedToFloat(callSite: IrCall, args: List<LLVMValueRef>) =
LLVMBuildUIToFP(builder, args[0], callSite.llvmReturnType, "")!!
private fun FunctionGenerationContext.emitFloatToSigned(callSite: IrCall, args: List<LLVMValueRef>) =
LLVMBuildFPToSI(builder, args[0], callSite.llvmReturnType, "")!!
private fun FunctionGenerationContext.emitFloatExtend(callSite: IrCall, args: List<LLVMValueRef>) =
LLVMBuildFPExt(builder, args[0], callSite.llvmReturnType, "")!!
private fun FunctionGenerationContext.emitFloatTruncate(callSite: IrCall, args: List<LLVMValueRef>) =
LLVMBuildFPTrunc(builder, args[0], callSite.llvmReturnType, "")!!
private fun FunctionGenerationContext.emitShift(op: LLVMOpcode, args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
val shift = if (first.type == int64Type) {
val tmp = and(second, Int32(63).llvm)
zext(tmp, int64Type)
} else {
and(second, Int32(31).llvm)
}
return LLVMBuildBinOp(builder, op, first, shift, "")!!
}
private fun FunctionGenerationContext.emitShl(args: List<LLVMValueRef>) =
emitShift(LLVMOpcode.LLVMShl, args)
private fun FunctionGenerationContext.emitShr(args: List<LLVMValueRef>) =
emitShift(LLVMOpcode.LLVMAShr, args)
private fun FunctionGenerationContext.emitUshr(args: List<LLVMValueRef>) =
emitShift(LLVMOpcode.LLVMLShr, args)
private fun FunctionGenerationContext.emitAnd(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return and(first, second)
}
private fun FunctionGenerationContext.emitOr(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return or(first, second)
}
private fun FunctionGenerationContext.emitXor(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return xor(first, second)
}
private fun FunctionGenerationContext.emitInv(args: List<LLVMValueRef>): LLVMValueRef {
val first = args[0]
val mask = makeConstOfType(first.type, -1)
return xor(first, mask)
}
private fun FunctionGenerationContext.emitMinus(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return if (first.type.isFloatingPoint()) {
fsub(first, second)
} else {
sub(first, second)
}
}
private fun FunctionGenerationContext.emitTimes(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
return if (first.type.isFloatingPoint()) {
LLVMBuildFMul(builder, first, second, "")
} else {
LLVMBuildMul(builder, first, second, "")
}!!
}
private fun FunctionGenerationContext.emitThrowIfZero(divider: LLVMValueRef) {
ifThen(icmpEq(divider, Zero(divider.type).llvm)) {
val throwArithExc = codegen.llvmFunction(context.ir.symbols.throwArithmeticException.owner)
call(throwArithExc, emptyList(), Lifetime.GLOBAL, environment.exceptionHandler)
unreachable()
}
}
private fun FunctionGenerationContext.emitThrowIfOOB(index: LLVMValueRef, size: LLVMValueRef) {
ifThen(icmpUGe(index, size)) {
val throwIndexOutOfBoundsException = codegen.llvmFunction(context.ir.symbols.throwIndexOutOfBoundsException.owner)
call(throwIndexOutOfBoundsException, emptyList(), Lifetime.GLOBAL, environment.exceptionHandler)
unreachable()
}
}
private fun FunctionGenerationContext.emitSignedDiv(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
if (!second.type.isFloatingPoint()) {
emitThrowIfZero(second)
}
return if (first.type.isFloatingPoint()) {
LLVMBuildFDiv(builder, first, second, "")
} else {
LLVMBuildSDiv(builder, first, second, "")
}!!
}
private fun FunctionGenerationContext.emitSignedRem(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
if (!second.type.isFloatingPoint()) {
emitThrowIfZero(second)
}
return if (first.type.isFloatingPoint()) {
LLVMBuildFRem(builder, first, second, "")
} else {
LLVMBuildSRem(builder, first, second, "")
}!!
}
private fun FunctionGenerationContext.emitUnsignedDiv(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
emitThrowIfZero(second)
return LLVMBuildUDiv(builder, first, second, "")!!
}
private fun FunctionGenerationContext.emitUnsignedRem(args: List<LLVMValueRef>): LLVMValueRef {
val (first, second) = args
emitThrowIfZero(second)
return LLVMBuildURem(builder, first, second, "")!!
}
private fun FunctionGenerationContext.emitInc(args: List<LLVMValueRef>): LLVMValueRef {
val first = args[0]
val const1 = makeConstOfType(first.type, 1)
return if (first.type.isFloatingPoint()) {
fadd(first, const1)
} else {
add(first, const1)
}
}
private fun FunctionGenerationContext.emitDec(args: List<LLVMValueRef>): LLVMValueRef {
val first = args[0]
val const1 = makeConstOfType(first.type, 1)
return if (first.type.isFloatingPoint()) {
fsub(first, const1)
} else {
sub(first, const1)
}
}
private fun FunctionGenerationContext.emitUnaryPlus(args: List<LLVMValueRef>) =
args[0]
private fun FunctionGenerationContext.emitUnaryMinus(args: List<LLVMValueRef>): LLVMValueRef {
val first = args[0]
val destTy = first.type
return if (destTy.isFloatingPoint()) {
fneg(first)
} else {
val const0 = makeConstOfType(destTy, 0)
sub(const0, first)
}
}
private fun FunctionGenerationContext.emitCompareTo(args: List<LLVMValueRef>, signed: Boolean): LLVMValueRef {
val (first, second) = args
val equal = icmpEq(first, second)
val less = if (signed) icmpLt(first, second) else icmpULt(first, second)
val tmp = select(less, Int32(-1).llvm, Int32(1).llvm)
return select(equal, Int32(0).llvm, tmp)
}
private fun FunctionGenerationContext.emitSignedCompareTo(args: List<LLVMValueRef>) =
emitCompareTo(args, signed = true)
private fun FunctionGenerationContext.emitUnsignedCompareTo(args: List<LLVMValueRef>) =
emitCompareTo(args, signed = false)
private fun makeConstOfType(type: LLVMTypeRef, value: Int): LLVMValueRef = when (type) {
int8Type -> Int8(value.toByte()).llvm
int16Type -> Char16(value.toChar()).llvm
int32Type -> Int32(value).llvm
int64Type -> Int64(value.toLong()).llvm
floatType -> Float32(value.toFloat()).llvm
doubleType -> Float64(value.toDouble()).llvm
else -> context.reportCompilationError("Unexpected primitive type: $type")
}
}
@@ -0,0 +1,172 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMLinkage
import llvm.LLVMSetLinkage
import llvm.LLVMValueRef
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
internal class KotlinObjCClassInfoGenerator(override val context: Context) : ContextUtils {
fun generate(irClass: IrClass) {
assert(irClass.isFinalClass)
val objCLLvmDeclarations = context.llvmDeclarations.forClass(irClass).objCDeclarations!!
val instanceMethods = generateInstanceMethodDescs(irClass)
val companionObject = irClass.companionObject()
val classMethods = companionObject?.generateMethodDescs().orEmpty()
val superclassName = irClass.getSuperClassNotAny()!!.let {
context.llvm.imports.add(it.llvmSymbolOrigin)
it.descriptor.getExternalObjCClassBinaryName()
}
val protocolNames = irClass.getSuperInterfaces().map {
context.llvm.imports.add(it.llvmSymbolOrigin)
it.name.asString().removeSuffix("Protocol")
}
val exportedClassName = selectExportedClassName(irClass)
val className = exportedClassName ?: selectInternalClassName(irClass)
val classNameLiteral = className?.let { staticData.cStringLiteral(it) } ?: NullPointer(int8Type)
val info = Struct(runtime.kotlinObjCClassInfo,
classNameLiteral,
Int32(if (exportedClassName != null) 1 else 0),
staticData.cStringLiteral(superclassName),
staticData.placeGlobalConstArray("", int8TypePtr,
protocolNames.map { staticData.cStringLiteral(it) } + NullPointer(int8Type)),
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, instanceMethods),
Int32(instanceMethods.size),
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, classMethods),
Int32(classMethods.size),
objCLLvmDeclarations.bodyOffsetGlobal.pointer,
irClass.typeInfoPtr,
companionObject?.typeInfoPtr ?: NullPointer(runtime.typeInfoType),
staticData.placeGlobal(
"kobjcclassptr:${irClass.fqNameForIrSerialization}#internal",
NullPointer(int8Type)
).pointer,
generateClassDataImp(irClass)
)
objCLLvmDeclarations.classInfoGlobal.setInitializer(info)
objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0))
}
private fun IrClass.generateMethodDescs(): List<ObjCMethodDesc> = this.generateImpMethodDescs()
private fun generateInstanceMethodDescs(
irClass: IrClass
): List<ObjCMethodDesc> = mutableListOf<ObjCMethodDesc>().apply {
addAll(irClass.generateMethodDescs())
val allImplementedSelectors = this.map { it.selector }.toSet()
assert(irClass.getSuperClassNotAny()!!.isExternalObjCClass())
val allInitMethodsInfo = irClass.getSuperClassNotAny()!!.constructors
.mapNotNull { it.getObjCInitMethod()?.getExternalObjCMethodInfo() }
.filter { it.selector !in allImplementedSelectors }
.distinctBy { it.selector }
allInitMethodsInfo.mapTo(this) {
ObjCMethodDesc(it.selector, it.encoding, context.llvm.missingInitImp)
}
}
private fun selectExportedClassName(irClass: IrClass): String? {
val exportObjCClassAnnotation = context.interopBuiltIns.exportObjCClass.fqNameSafe
val explicitName = irClass.getAnnotationArgumentValue<String>(exportObjCClassAnnotation, "name")
if (explicitName != null) return explicitName
return if (irClass.annotations.hasAnnotation(exportObjCClassAnnotation)) irClass.name.asString() else null
}
private fun selectInternalClassName(irClass: IrClass): String? = if (irClass.isExported()) {
irClass.fqNameForIrSerialization.asString()
} else {
null // Generate as anonymous.
}
private val impType = pointerType(functionType(int8TypePtr, true, int8TypePtr, int8TypePtr))
private inner class ObjCMethodDesc(
val selector: String, val encoding: String, val impFunction: LLVMValueRef
) : Struct(
runtime.objCMethodDescription,
constPointer(impFunction).bitcast(impType),
staticData.cStringLiteral(selector),
staticData.cStringLiteral(encoding)
)
private fun IrClass.generateImpMethodDescs(): List<ObjCMethodDesc> = this.declarations
.filterIsInstance<IrSimpleFunction>()
.mapNotNull {
val annotation =
it.annotations.findAnnotation(context.interopBuiltIns.objCMethodImp.fqNameSafe) ?:
return@mapNotNull null
ObjCMethodDesc(
annotation.getAnnotationStringValue("selector"),
annotation.getAnnotationStringValue("encoding"),
it.llvmFunction
)
}
private fun generateClassDataImp(irClass: IrClass): ConstPointer {
val classDataPointer = staticData.placeGlobal(
"kobjcclassdata:${irClass.fqNameForIrSerialization}#internal",
Zero(runtime.kotlinObjCClassData)
).pointer
val functionType = functionType(classDataPointer.llvmType, false, int8TypePtr, int8TypePtr)
val functionName = "kobjcclassdataimp:${irClass.fqNameForIrSerialization}#internal"
val function = generateFunctionNoRuntime(codegen, functionType, functionName) {
ret(classDataPointer.llvm)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
}
return constPointer(function)
}
private val codegen = CodeGenerator(context)
companion object {
const val createdClassFieldIndex = 11
}
}
internal fun CodeGenerator.kotlinObjCClassInfo(irClass: IrClass): LLVMValueRef {
require(irClass.isKotlinObjCClass())
return if (isExternal(irClass)) {
importGlobal(
irClass.kotlinObjCClassInfoSymbolName,
runtime.kotlinObjCClassInfo,
origin = irClass.llvmSymbolOrigin
)
} else {
context.llvmDeclarations.forClass(irClass).objCDeclarations!!.classInfoGlobal.llvmGlobal
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.isThrowable
import org.jetbrains.kotlin.konan.target.Family
internal fun addLlvmFunctionWithDefaultAttributes(
context: Context,
module: LLVMModuleRef,
name: String,
type: LLVMTypeRef
): LLVMValueRef = LLVMAddFunction(module, name, type)!!.also {
addDefaultLlvmFunctionAttributes(context, it)
}
/**
* Mimics parts of clang's `CodeGenModule::getDefaultFunctionAttributes`
* that are required for Kotlin/Native compiler.
*/
private fun addDefaultLlvmFunctionAttributes(context: Context, llvmFunction: LLVMValueRef) {
if (shouldEnforceFramePointer(context)) {
// Note: this is default for clang on at least on iOS and macOS.
enforceFramePointer(llvmFunction)
}
}
internal fun addLlvmAttributesForKotlinFunction(context: Context, irFunction: IrFunction, llvmFunction: LLVMValueRef) {
if (irFunction.returnType.isNothing()) {
setFunctionNoReturn(llvmFunction)
}
if (mustNotInline(context, irFunction)) {
setFunctionNoInline(llvmFunction)
}
}
private fun mustNotInline(context: Context, irFunction: IrFunction): Boolean {
if (context.shouldContainLocationDebugInfo()) {
if (irFunction is IrConstructor && irFunction.isPrimary && irFunction.returnType.isThrowable()) {
// To simplify skipping this constructor when scanning call stack in Kotlin_getCurrentStackTrace.
return true
}
}
return false
}
private fun shouldEnforceFramePointer(context: Context): Boolean {
// TODO: do we still need it?
if (!context.shouldOptimize()) {
return true
}
return when (context.config.target.family) {
Family.OSX, Family.IOS, Family.WATCHOS, Family.TVOS -> context.shouldContainLocationDebugInfo()
Family.LINUX, Family.MINGW, Family.ANDROID, Family.WASM, Family.ZEPHYR -> false
}
}
private fun enforceFramePointer(llvmFunction: LLVMValueRef) {
LLVMAddTargetDependentFunctionAttr(llvmFunction, "no-frame-pointer-elim", "true")
LLVMAddTargetDependentFunctionAttr(llvmFunction, "no-frame-pointer-elim-non-leaf", "")
}
@@ -0,0 +1,416 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal fun createLlvmDeclarations(context: Context): LlvmDeclarations {
val generator = DeclarationsGeneratorVisitor(context)
context.ir.irModule.acceptChildrenVoid(generator)
return LlvmDeclarations(generator.uniques)
}
// Please note, that llvmName is part of the ABI, and cannot be liberally changed.
enum class UniqueKind(val llvmName: String) {
UNIT("theUnitInstance"),
EMPTY_ARRAY("theEmptyArray")
}
internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
fun forFunction(function: IrFunction) = forFunctionOrNull(function) ?: with(function){error("$name in $file/${parent.fqNameForIrSerialization}")}
fun forFunctionOrNull(function: IrFunction) = (function.metadata as? CodegenFunctionMetadata)?.llvm
fun forClass(irClass: IrClass) = (irClass.metadata as? CodegenClassMetadata)?.llvm ?:
error(irClass.descriptor.toString())
fun forField(field: IrField) = (field.metadata as? CodegenInstanceFieldMetadata)?.llvm ?:
error(field.descriptor.toString())
fun forStaticField(field: IrField) = (field.metadata as? CodegenStaticFieldMetadata)?.llvm ?:
error(field.descriptor.toString())
fun forSingleton(irClass: IrClass) = forClass(irClass).singletonDeclarations ?:
error(irClass.descriptor.toString())
fun forUnique(kind: UniqueKind) = unique[kind] ?: error("No unique $kind")
}
internal class ClassLlvmDeclarations(
val bodyType: LLVMTypeRef,
val typeInfoGlobal: StaticData.Global,
val writableTypeInfoGlobal: StaticData.Global?,
val typeInfo: ConstPointer,
val singletonDeclarations: SingletonLlvmDeclarations?,
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
internal class SingletonLlvmDeclarations(val instanceStorage: AddressAccess)
internal class KotlinObjCClassLlvmDeclarations(
val classInfoGlobal: StaticData.Global,
val bodyOffsetGlobal: StaticData.Global
)
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
internal class StaticFieldLlvmDeclarations(val storageAddressAccess: AddressAccess)
internal class UniqueLlvmDeclarations(val pointer: ConstPointer)
private fun ContextUtils.createClassBodyType(name: String, fields: List<IrField>): LLVMTypeRef {
val fieldTypes = listOf(runtime.objHeaderType) + fields.map { getLLVMType(it.type) }
// TODO: consider adding synthetic ObjHeader field to Any.
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
// LLVMStructSetBody expects the struct to be properly aligned and will insert padding accordingly. In our case
// `allocInstance` returns 16x + 8 address, i.e. always misaligned for vector types. Workaround is to use packed struct.
val hasBigAlignment = fields.any { LLVMABIAlignmentOfType(context.llvm.runtime.targetData, getLLVMType(it.type)) > 8 }
val packed = if (hasBigAlignment) 1 else 0
LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, packed)
return classType
}
private class DeclarationsGeneratorVisitor(override val context: Context) :
IrElementVisitorVoid, ContextUtils {
val uniques = mutableMapOf<UniqueKind, UniqueLlvmDeclarations>()
private class Namer(val prefix: String) {
private val names = mutableMapOf<IrDeclaration, Name>()
private val counts = mutableMapOf<FqName, Int>()
fun getName(parent: FqName, declaration: IrDeclaration): Name {
return names.getOrPut(declaration) {
val count = counts.getOrDefault(parent, 0) + 1
counts[parent] = count
Name.identifier(prefix + count)
}
}
}
val objectNamer = Namer("object-")
private fun getLocalName(parent: FqName, declaration: IrDeclaration): Name {
if (declaration.isAnonymousObject) {
return objectNamer.getName(parent, declaration)
}
return declaration.nameForIrSerialization
}
private fun getFqName(declaration: IrDeclaration): FqName {
val parent = declaration.parent
val parentFqName = when (parent) {
is IrPackageFragment -> parent.fqName
is IrDeclaration -> getFqName(parent)
else -> error(parent)
}
val localName = getLocalName(parentFqName, declaration)
return parentFqName.child(localName)
}
/**
* Produces the name to be used for non-exported LLVM declarations corresponding to [declaration].
*
* Note: since these declarations are going to be private, the name is only required not to clash with any
* exported declarations.
*/
private fun qualifyInternalName(declaration: IrDeclaration): String {
return getFqName(declaration).asString() + "#internal"
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
if (declaration.requiresRtti()) {
val classLlvmDeclarations = createClassDeclarations(declaration)
val metadata = declaration.metadata as? CodegenClassMetadata
?: CodegenClassMetadata(declaration).also { declaration.metadata = it }
metadata.llvm = classLlvmDeclarations
}
super.visitClass(declaration)
}
private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations {
val internalName = qualifyInternalName(declaration)
val fields = context.getLayoutBuilder(declaration).fields
val bodyType = createClassBodyType("kclassbody:$internalName", fields)
val typeInfoPtr: ConstPointer
val typeInfoGlobal: StaticData.Global
val typeInfoSymbolName = if (declaration.isExported()) {
declaration.computeTypeInfoSymbolName()
} else {
"ktype:$internalName"
}
if (declaration.typeInfoHasVtableAttached) {
// Create the special global consisting of TypeInfo and vtable.
val typeInfoGlobalName = "ktypeglobal:$internalName"
val typeInfoWithVtableType = structType(
runtime.typeInfoType,
LLVMArrayType(int8TypePtr, context.getLayoutBuilder(declaration).vtableEntries.size)!!
)
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
val llvmTypeInfoPtr = LLVMAddAlias(context.llvmModule,
kTypeInfoPtr,
typeInfoGlobal.pointer.getElementPtr(0).llvm,
typeInfoSymbolName)!!
if (declaration.isExported()) {
if (llvmTypeInfoPtr.name != typeInfoSymbolName) {
// So alias name has been mangled by LLVM to avoid name clash.
throw IllegalArgumentException("Global '$typeInfoSymbolName' already exists")
}
} else {
LLVMSetLinkage(llvmTypeInfoPtr, LLVMLinkage.LLVMInternalLinkage)
}
typeInfoPtr = constPointer(llvmTypeInfoPtr)
} else {
typeInfoGlobal = staticData.createGlobal(runtime.typeInfoType,
typeInfoSymbolName,
isExported = declaration.isExported())
typeInfoPtr = typeInfoGlobal.pointer
}
if (declaration.isUnit() || declaration.isKotlinArray())
createUniqueDeclarations(declaration, typeInfoPtr, bodyType)
val singletonDeclarations = if (declaration.kind.isSingleton) {
createSingletonDeclarations(declaration)
} else {
null
}
val objCDeclarations = if (declaration.isKotlinObjCClass()) {
createKotlinObjCClassDeclarations(declaration)
} else {
null
}
val writableTypeInfoType = runtime.writableTypeInfoType
val writableTypeInfoGlobal = if (writableTypeInfoType == null) {
null
} else if (declaration.isExported()) {
val name = declaration.writableTypeInfoSymbolName
staticData.createGlobal(writableTypeInfoType, name, isExported = true).also {
it.setLinkage(LLVMLinkage.LLVMCommonLinkage) // Allows to be replaced by other bitcode module.
}
} else {
staticData.createGlobal(writableTypeInfoType, "")
}.also {
it.setZeroInitializer()
}
return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr,
singletonDeclarations, objCDeclarations)
}
private fun createUniqueDeclarations(
irClass: IrClass, typeInfoPtr: ConstPointer, bodyType: LLVMTypeRef) {
when {
irClass.isUnit() -> {
uniques[UniqueKind.UNIT] =
UniqueLlvmDeclarations(staticData.createUniqueInstance(UniqueKind.UNIT, bodyType, typeInfoPtr))
}
irClass.isKotlinArray() -> {
uniques[UniqueKind.EMPTY_ARRAY] =
UniqueLlvmDeclarations(staticData.createUniqueInstance(UniqueKind.EMPTY_ARRAY, bodyType, typeInfoPtr))
}
else -> TODO("Unsupported unique $irClass")
}
}
private fun createSingletonDeclarations(irClass: IrClass): SingletonLlvmDeclarations? {
if (irClass.isUnit()) {
return null
}
val storageKind = irClass.storageKind(context)
val threadLocal = storageKind == ObjectStorageKind.THREAD_LOCAL
val isExported = irClass.isExported()
val symbolName = if (isExported) {
irClass.globalObjectStorageSymbolName
} else {
"kobjref:" + qualifyInternalName(irClass)
}
val instanceAddress = if (threadLocal) {
addKotlinThreadLocal(symbolName, getLLVMType(irClass.defaultType))
} else {
addKotlinGlobal(symbolName, getLLVMType(irClass.defaultType), isExported)
}
return SingletonLlvmDeclarations(instanceAddress)
}
private fun createKotlinObjCClassDeclarations(irClass: IrClass): KotlinObjCClassLlvmDeclarations {
val internalName = qualifyInternalName(irClass)
val isExported = irClass.isExported()
val classInfoSymbolName = if (isExported) {
irClass.kotlinObjCClassInfoSymbolName
} else {
"kobjcclassinfo:$internalName"
}
val classInfoGlobal = staticData.createGlobal(
context.llvm.runtime.kotlinObjCClassInfo,
classInfoSymbolName,
isExported = isExported
).apply {
setConstant(true)
}
val bodyOffsetGlobal = staticData.createGlobal(int32Type, "kobjcbodyoffs:$internalName")
return KotlinObjCClassLlvmDeclarations(classInfoGlobal, bodyOffsetGlobal)
}
override fun visitField(declaration: IrField) {
super.visitField(declaration)
val containingClass = declaration.parent as? IrClass
if (containingClass != null) {
if (!containingClass.requiresRtti()) return
val classDeclarations = (containingClass.metadata as? CodegenClassMetadata)?.llvm
?: error(containingClass.descriptor.toString())
val allFields = context.getLayoutBuilder(containingClass).fields
declaration.metadata = CodegenInstanceFieldMetadata(
declaration.metadata?.name,
containingClass.konanLibrary,
FieldLlvmDeclarations(
allFields.indexOf(declaration) + 1, // First field is ObjHeader.
classDeclarations.bodyType
)
)
} else {
// Fields are module-private, so we use internal name:
val name = "kvar:" + qualifyInternalName(declaration)
val storage = if (declaration.storageKind == FieldStorageKind.THREAD_LOCAL) {
addKotlinThreadLocal(name, getLLVMType(declaration.type))
} else {
addKotlinGlobal(name, getLLVMType(declaration.type), isExported = false)
}
declaration.metadata = CodegenStaticFieldMetadata(
declaration.metadata?.name,
declaration.konanLibrary,
StaticFieldLlvmDeclarations(storage)
)
}
}
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
if (!declaration.isReal) return
val llvmFunctionType = getLlvmFunctionType(declaration)
if ((declaration is IrConstructor && declaration.isObjCConstructor)) {
return
}
val llvmFunction = if (declaration.isExternal) {
if (declaration.isTypedIntrinsic || declaration.isObjCBridgeBased()
// All call-sites to external accessors to interop properties
// are lowered by InteropLowering.
|| (declaration.isAccessor && declaration.isFromInteropLibrary())
|| declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return
context.llvm.externalFunction(declaration.computeSymbolName(), llvmFunctionType,
// Assume that `external fun` is defined in native libs attached to this module:
origin = declaration.llvmSymbolOrigin,
independent = declaration.hasAnnotation(RuntimeNames.independent)
)
} else {
val symbolName = if (declaration.isExported()) {
declaration.computeSymbolName().also {
if (declaration.name.asString() != "main") {
assert(LLVMGetNamedFunction(context.llvm.llvmModule, it) == null) { it }
} else {
// As a workaround, allow `main` functions to clash because frontend accepts this.
// See [OverloadResolver.isTopLevelMainInDifferentFiles] usage.
}
}
} else {
"kfun:" + qualifyInternalName(declaration)
}
addLlvmFunctionWithDefaultAttributes(
context,
context.llvmModule!!,
symbolName,
llvmFunctionType
).also {
addLlvmAttributesForKotlinFunction(context, declaration, it)
}
}
declaration.metadata = CodegenFunctionMetadata(
declaration.metadata?.name,
declaration.konanLibrary,
FunctionLlvmDeclarations(llvmFunction)
)
}
}
internal open class KonanMetadata(override val name: Name?, val konanLibrary: KotlinLibrary?) : MetadataSource
internal class CodegenClassMetadata(irClass: IrClass)
: KonanMetadata(irClass.metadata?.name, irClass.konanLibrary), MetadataSource.Class {
var layoutBuilder: ClassLayoutBuilder? = null
var llvm: ClassLlvmDeclarations? = null
}
private class CodegenFunctionMetadata(
name: Name?,
konanLibrary: KotlinLibrary?,
val llvm: FunctionLlvmDeclarations
) : KonanMetadata(name, konanLibrary), MetadataSource.Function
private class CodegenInstanceFieldMetadata(
name: Name?,
konanLibrary: KotlinLibrary?,
val llvm: FieldLlvmDeclarations
) : KonanMetadata(name, konanLibrary), MetadataSource.Property {
override val isConst = false
}
private class CodegenStaticFieldMetadata(
name: Name?,
konanLibrary: KotlinLibrary?,
val llvm: StaticFieldLlvmDeclarations
) : KonanMetadata(name, konanLibrary), MetadataSource.Property {
override val isConst = false
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMAddNamedMetadataOperand
import llvm.LLVMModuleRef
fun embedLlvmLinkOptions(module: LLVMModuleRef, options: List<List<String>>) {
options.forEach {
val node = node(*it.map { it.mdString() }.toTypedArray())
LLVMAddNamedMetadataOperand(module, "llvm.linker.options", node)
}
}
@@ -0,0 +1,440 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
private val llvmContextHolder = ThreadLocal<LLVMContextRef>()
internal var llvmContext: LLVMContextRef
get() = llvmContextHolder.get()
set(value) { llvmContextHolder.set(value) }
internal fun tryDisposeLLVMContext() {
val llvmContext = llvmContextHolder.get()
if (llvmContext != null)
LLVMContextDispose(llvmContext)
llvmContextHolder.remove()
}
internal val LLVMTypeRef.context: LLVMContextRef
get() = LLVMGetTypeContext(this)!!
internal val List<LLVMTypeRef>.context: LLVMContextRef
get() {
val context = this[0].context
for (i in 1 until this.size)
assert(this[i].context == context) {
"Expected the same context for all types in a list"
}
return context
}
internal val LLVMValueRef.type: LLVMTypeRef
get() = LLVMTypeOf(this)!!
/**
* Represents the value which can be emitted as bitcode const value
*/
internal interface ConstValue {
val llvm: LLVMValueRef
}
internal val ConstValue.llvmType: LLVMTypeRef
get() = this.llvm.type
internal interface ConstPointer : ConstValue {
fun getElementPtr(index: Int): ConstPointer = ConstGetElementPtr(this, index)
}
internal fun constPointer(value: LLVMValueRef) = object : ConstPointer {
init {
assert(LLVMIsConstant(value) == 1)
}
override val llvm = value
}
private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : ConstPointer {
override val llvm = LLVMConstInBoundsGEP(pointer.llvm, cValuesOf(Int32(0).llvm, Int32(index).llvm), 2)!!
// TODO: squash multiple GEPs
}
internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.llvm, toType)!!)
internal class ConstArray(elementType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
init {
elements.forEach {
assert(it.llvmType == elementType) {
"Expected element type: ${llvmtype2string(elementType)}, actual: ${llvmtype2string(it.llvmType)}"
}
}
}
override val llvm = LLVMConstArray(elementType, elements.map { it.llvm }.toCValues(), elements.size)!!
}
internal open class Struct(val type: LLVMTypeRef?, val elements: List<ConstValue?>) : ConstValue {
constructor(type: LLVMTypeRef?, vararg elements: ConstValue?) : this(type, elements.toList())
constructor(vararg elements: ConstValue) : this(structType(elements.map { it.llvmType }), *elements)
override val llvm = LLVMConstNamedStruct(type, elements.mapIndexed { index, element ->
val expectedType = LLVMStructGetTypeAtIndex(type, index)
if (element == null) {
LLVMConstNull(expectedType)!!
} else {
element.llvm.also {
assert(it.type == expectedType) {
"Unexpected type at $index: expected ${LLVMPrintTypeToString(expectedType)!!.toKString()} " +
"got ${LLVMPrintTypeToString(it.type)!!.toKString()}"
}
}
}
}.toCValues(), elements.size)!!
init {
assert(elements.size == LLVMCountStructElementTypes(type))
}
}
internal val int1Type get() = LLVMInt1TypeInContext(llvmContext)!!
internal val int8Type get() = LLVMInt8TypeInContext(llvmContext)!!
internal val int16Type get() = LLVMInt16TypeInContext(llvmContext)!!
internal val int32Type get() = LLVMInt32TypeInContext(llvmContext)!!
internal val int64Type get() = LLVMInt64TypeInContext(llvmContext)!!
internal val int8TypePtr get() = pointerType(int8Type)
internal val floatType get() = LLVMFloatTypeInContext(llvmContext)!!
internal val doubleType get() = LLVMDoubleTypeInContext(llvmContext)!!
internal val vector128Type get() = LLVMVectorType(floatType, 4)!!
internal val voidType get() = LLVMVoidTypeInContext(llvmContext)!!
internal class Int1(val value: Byte) : ConstValue {
override val llvm = LLVMConstInt(int1Type, value.toLong(), 1)!!
}
internal class Int8(val value: Byte) : ConstValue {
override val llvm = LLVMConstInt(int8Type, value.toLong(), 1)!!
}
internal class Int16(val value: Short) : ConstValue {
override val llvm = LLVMConstInt(int16Type, value.toLong(), 1)!!
}
internal class Char16(val value: Char) : ConstValue {
override val llvm = LLVMConstInt(int16Type, value.toLong(), 1)!!
}
internal class Int32(val value: Int) : ConstValue {
override val llvm = LLVMConstInt(int32Type, value.toLong(), 1)!!
}
internal class Int64(val value: Long) : ConstValue {
override val llvm = LLVMConstInt(int64Type, value, 1)!!
}
internal class Float32(val value: Float) : ConstValue {
override val llvm = LLVMConstReal(floatType, value.toDouble())!!
}
internal class Float64(val value: Double) : ConstValue {
override val llvm = LLVMConstReal(doubleType, value)!!
}
internal class Zero(val type: LLVMTypeRef) : ConstValue {
override val llvm = LLVMConstNull(type)!!
}
internal class NullPointer(pointeeType: LLVMTypeRef): ConstPointer {
override val llvm = LLVMConstNull(pointerType(pointeeType))!!
}
internal fun constValue(value: LLVMValueRef) = object : ConstValue {
init {
assert (LLVMIsConstant(value) == 1)
}
override val llvm = value
}
internal val RuntimeAware.kTypeInfo: LLVMTypeRef
get() = runtime.typeInfoType
internal val RuntimeAware.kObjHeader: LLVMTypeRef
get() = runtime.objHeaderType
internal val RuntimeAware.kObjHeaderPtr: LLVMTypeRef
get() = pointerType(kObjHeader)
internal val RuntimeAware.kObjHeaderPtrPtr: LLVMTypeRef
get() = pointerType(kObjHeaderPtr)
internal val RuntimeAware.kArrayHeader: LLVMTypeRef
get() = runtime.arrayHeaderType
internal val RuntimeAware.kArrayHeaderPtr: LLVMTypeRef
get() = pointerType(kArrayHeader)
internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
get() = pointerType(kTypeInfo)
internal val kInt1 get() = int1Type
internal val kBoolean get() = kInt1
internal val kInt8Ptr get() = pointerType(int8Type)
internal val kInt8PtrPtr get() = pointerType(kInt8Ptr)
internal val kNullInt8Ptr get() = LLVMConstNull(kInt8Ptr)!!
internal val kImmInt32Zero get() = Int32(0).llvm
internal val kImmInt32One get() = Int32(1).llvm
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef
get() = LLVMConstNull(this.kObjHeaderPtr)!!
internal val ContextUtils.kNullObjHeaderPtrPtr: LLVMValueRef
get() = LLVMConstNull(this.kObjHeaderPtrPtr)!!
// Nothing type has no values, but we do generate unreachable code and thus need some fake value:
internal val ContextUtils.kNothingFakeValue: LLVMValueRef
get() = LLVMGetUndef(kObjHeaderPtr)!!
internal fun pointerType(pointeeType: LLVMTypeRef) = LLVMPointerType(pointeeType, 0)!!
internal fun structType(vararg types: LLVMTypeRef): LLVMTypeRef = structType(types.toList())
internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef =
LLVMStructTypeInContext(llvmContext, types.toCValues(), types.size, 0)!!
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
// Note that type is usually function pointer, so we have to dereference it.
return LLVMCountParamTypes(LLVMGetElementType(functionType))
}
fun extractConstUnsignedInt(value: LLVMValueRef): Long {
assert(LLVMIsConstant(value) != 0)
return LLVMConstIntGetZExtValue(value)
}
internal fun ContextUtils.isObjectReturn(functionType: LLVMTypeRef) : Boolean {
// Note that type is usually function pointer, so we have to dereference it.
val returnType = LLVMGetReturnType(LLVMGetElementType(functionType))!!
return isObjectType(returnType)
}
internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
return isObjectType(value.type)
}
internal fun RuntimeAware.isObjectType(type: LLVMTypeRef): Boolean {
return type == kObjHeaderPtr || type == kArrayHeaderPtr
}
/**
* Reads [size] bytes contained in this array.
*/
internal fun CArrayPointer<ByteVar>.getBytes(size: Long) =
(0 .. size-1).map { this[it] }.toByteArray()
internal fun getFunctionType(ptrToFunction: LLVMValueRef): LLVMTypeRef {
return getGlobalType(ptrToFunction)
}
internal fun getGlobalType(ptrToGlobal: LLVMValueRef): LLVMTypeRef {
return LLVMGetElementType(ptrToGlobal.type)!!
}
internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef, isExported: Boolean): LLVMValueRef {
if (isExported)
assert(LLVMGetNamedGlobal(context.llvmModule, name) == null)
return LLVMAddGlobal(context.llvmModule, type, name)!!
}
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef {
context.llvm.imports.add(origin)
val found = LLVMGetNamedGlobal(context.llvmModule, name)
return if (found != null) {
assert (getGlobalType(found) == type)
assert (LLVMGetInitializer(found) == null) { "$name is already declared in the current module" }
found
} else {
addGlobal(name, type, isExported = false)
}
}
internal abstract class AddressAccess {
abstract fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef
}
internal class GlobalAddressAccess(private val address: LLVMValueRef): AddressAccess() {
override fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef = address
}
internal class TLSAddressAccess(
private val context: Context, private val index: Int): AddressAccess() {
override fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef {
return generationContext!!.call(context.llvm.lookupTLS,
listOf(context.llvm.tlsKey, Int32(index).llvm))
}
}
internal fun ContextUtils.addKotlinThreadLocal(name: String, type: LLVMTypeRef): AddressAccess {
return if (isObjectType(type)) {
val index = context.llvm.tlsCount++
TLSAddressAccess(context, index)
} else {
// TODO: This will break if Workers get decoupled from host threads.
GlobalAddressAccess(LLVMAddGlobal(context.llvmModule, type, name)!!.also {
LLVMSetThreadLocalMode(it, context.llvm.tlsMode)
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
})
}
}
internal fun ContextUtils.addKotlinGlobal(name: String, type: LLVMTypeRef, isExported: Boolean): AddressAccess {
return GlobalAddressAccess(LLVMAddGlobal(context.llvmModule, type, name)!!.also {
if (!isExported)
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
})
}
internal fun functionType(returnType: LLVMTypeRef, isVarArg: Boolean = false, vararg paramTypes: LLVMTypeRef) =
LLVMFunctionType(
returnType,
cValuesOf(*paramTypes), paramTypes.size,
if (isVarArg) 1 else 0
)!!
internal fun functionType(returnType: LLVMTypeRef, isVarArg: Boolean = false, paramTypes: List<LLVMTypeRef>) =
functionType(returnType, isVarArg, *paramTypes.toTypedArray())
fun llvm2string(value: LLVMValueRef?): String {
if (value == null) return "<null>"
return LLVMPrintValueToString(value)!!.toKString()
}
fun llvmtype2string(type: LLVMTypeRef?): String {
if (type == null) return "<null type>"
return LLVMPrintTypeToString(type)!!.toKString()
}
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
val count = LLVMCountStructElementTypes(type)
return (0 until count).map {
LLVMStructGetTypeAtIndex(type, it)!!
}
}
fun parseBitcodeFile(path: String): LLVMModuleRef = memScoped {
val bufRef = alloc<LLVMMemoryBufferRefVar>()
val errorRef = allocPointerTo<ByteVar>()
val res = LLVMCreateMemoryBufferWithContentsOfFile(path, bufRef.ptr, errorRef.ptr)
if (res != 0) {
throw Error(errorRef.value?.toKString())
}
val memoryBuffer = bufRef.value
try {
val moduleRef = alloc<LLVMModuleRefVar>()
val parseRes = LLVMParseBitcodeInContext2(llvmContext, memoryBuffer, moduleRef.ptr)
if (parseRes != 0) {
throw Error(parseRes.toString())
}
moduleRef.value!!
} finally {
LLVMDisposeMemoryBuffer(memoryBuffer)
}
}
private val nounwindAttrKindId by lazy {
getLlvmAttributeKindId("nounwind")
}
private val noreturnAttrKindId by lazy {
getLlvmAttributeKindId("noreturn")
}
private val noinlineAttrKindId by lazy {
getLlvmAttributeKindId("noinline")
}
private val signextAttrKindId by lazy {
getLlvmAttributeKindId("signext")
}
fun isFunctionNoUnwind(function: LLVMValueRef): Boolean {
val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, nounwindAttrKindId.value)
return attribute != null
}
internal fun getLlvmAttributeKindId(attributeName: String): LLVMAttributeKindId {
val attrKindId = LLVMGetEnumAttributeKindForName(attributeName, attributeName.length.signExtend())
if (attrKindId == 0) {
throw Error("Unable to find '$attributeName' attribute kind id")
}
return LLVMAttributeKindId(attrKindId)
}
data class LLVMAttributeKindId(val value: Int)
fun setFunctionNoUnwind(function: LLVMValueRef) {
addLlvmFunctionEnumAttribute(function, nounwindAttrKindId)
}
fun setFunctionNoReturn(function: LLVMValueRef) {
addLlvmFunctionEnumAttribute(function, noreturnAttrKindId)
}
fun setFunctionNoInline(function: LLVMValueRef) {
addLlvmFunctionEnumAttribute(function, noinlineAttrKindId)
}
internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attrKindId: LLVMAttributeKindId, value: Long = 0) {
val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, attrKindId, value)
addLlvmFunctionAttribute(function, attribute)
}
internal fun createLlvmEnumAttribute(llvmContext: LLVMContextRef, attrKindId: LLVMAttributeKindId, value: Long = 0) =
LLVMCreateEnumAttribute(llvmContext, attrKindId.value, value)!!
internal fun addLlvmFunctionAttribute(function: LLVMValueRef, attribute: LLVMAttributeRef) {
LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex, attribute)
}
fun addFunctionSignext(function: LLVMValueRef, index: Int, type: LLVMTypeRef?) {
if (type == int1Type || type == int8Type || type == int16Type) {
val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, signextAttrKindId)
LLVMAddAttributeAtIndex(function, index, attribute)
}
}
internal fun String.mdString() = LLVMMDStringInContext(llvmContext, this, this.length)!!
internal fun node(vararg it:LLVMValueRef) = LLVMMDNodeInContext(llvmContext, it.toList().toCValues(), it.size)
internal fun LLVMValueRef.setUnaligned() = apply { LLVMSetAlignment(this, 1) }
internal fun getOperands(value: LLVMValueRef) =
(0 until LLVMGetNumOperands(value)).map { LLVMGetOperand(value, it)!! }
internal fun getGlobalAliases(module: LLVMModuleRef) =
generateSequence(LLVMGetFirstGlobalAlias(module), { LLVMGetNextGlobalAlias(it) })
internal fun getFunctions(module: LLVMModuleRef) =
generateSequence(LLVMGetFirstFunction(module), { LLVMGetNextFunction(it) })
internal fun getGlobals(module: LLVMModuleRef) =
generateSequence(LLVMGetFirstGlobal(module), { LLVMGetNextGlobal(it) })
fun LLVMTypeRef.isFloatingPoint(): Boolean = when (llvm.LLVMGetTypeKind(this)) {
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind -> true
else -> false
}
fun LLVMTypeRef.isVectorElementType(): Boolean = when (llvm.LLVMGetTypeKind(this)) {
LLVMTypeKind.LLVMIntegerTypeKind,
LLVMTypeKind.LLVMFloatTypeKind,
LLVMTypeKind.LLVMDoubleTypeKind -> true
else -> false
}
@@ -0,0 +1,646 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.isPublicApi
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.name.FqName
internal class RTTIGenerator(override val context: Context) : ContextUtils {
private val acyclicCache = mutableMapOf<IrType, Boolean>()
private val safeAcyclicFieldTypes = setOf(
context.irBuiltIns.stringClass,
context.irBuiltIns.booleanClass, context.irBuiltIns.charClass,
context.irBuiltIns.byteClass, context.irBuiltIns.shortClass, context.irBuiltIns.intClass,
context.irBuiltIns.longClass,
context.irBuiltIns.floatClass,context.irBuiltIns.doubleClass) +
context.ir.symbols.primitiveArrays.values +
context.ir.symbols.unsignedArrays.values
// TODO: extend logic here by taking into account final acyclic classes.
private fun checkAcyclicFieldType(type: IrType): Boolean = acyclicCache.getOrPut(type) {
when {
type.isInterface() -> false
type.computePrimitiveBinaryTypeOrNull() != null -> true
else -> {
val classifier = type.classifierOrNull
(classifier != null && classifier in safeAcyclicFieldTypes)
}
}
}
private fun checkAcyclicClass(irClass: IrClass): Boolean = when {
irClass.symbol == context.ir.symbols.array -> false
irClass.isArray -> true
context.getLayoutBuilder(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true
else -> false
}
private fun flagsFromClass(irClass: IrClass): Int {
var result = 0
if (irClass.isFrozen)
result = result or TF_IMMUTABLE
// TODO: maybe perform deeper analysis to find surely acyclic types.
if (!irClass.isInterface && !irClass.isAbstract() && !irClass.isAnnotationClass) {
if (checkAcyclicClass(irClass)) {
result = result or TF_ACYCLIC
}
}
if (irClass.hasAnnotation(KonanFqNames.leakDetectorCandidate)) {
result = result or TF_LEAK_DETECTOR_CANDIDATE
}
if (irClass.isInterface)
result = result or TF_INTERFACE
if (irClass.defaultType.isSuspendFunction()) {
result = result or TF_SUSPEND_FUNCTION
}
if (irClass.hasAnnotation(KonanFqNames.hasFinalizer)) {
result = result or TF_HAS_FINALIZER
}
if (irClass.hasAnnotation(KonanFqNames.hasFreezeHook)) {
result = result or TF_HAS_FREEZE_HOOK
}
return result
}
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
inner class InterfaceTableRecord(id: Int32, vtableSize: Int32, vtable: ConstPointer?) :
Struct(runtime.interfaceTableRecordType, id, vtableSize, vtable)
private inner class TypeInfo(
selfPtr: ConstPointer,
extendedInfo: ConstPointer,
size: Int,
superType: ConstValue,
objOffsets: ConstValue,
objOffsetsCount: Int,
interfaces: ConstValue,
interfacesCount: Int,
methods: ConstValue,
methodsCount: Int,
interfaceTableSize: Int,
interfaceTable: ConstValue,
packageName: String?,
relativeName: String?,
flags: Int,
classId: Int,
writableTypeInfo: ConstPointer?,
associatedObjects: ConstPointer?) :
Struct(
runtime.typeInfoType,
selfPtr,
extendedInfo,
// TODO: it used to be a single int32 ABI version,
// but klib abi version is not an int anymore.
// So now this field is just reserved to preserve the layout.
Int32(0),
Int32(size),
superType,
objOffsets,
Int32(objOffsetsCount),
interfaces,
Int32(interfacesCount),
methods,
Int32(methodsCount),
Int32(interfaceTableSize),
interfaceTable,
kotlinStringLiteral(packageName),
kotlinStringLiteral(relativeName),
Int32(flags),
Int32(classId),
*listOfNotNull(writableTypeInfo).toTypedArray(),
associatedObjects
)
private fun kotlinStringLiteral(string: String?): ConstPointer = if (string == null) {
NullPointer(runtime.objHeaderType)
} else {
staticData.kotlinStringLiteral(string)
}
private val EXPORT_TYPE_INFO_FQ_NAME = FqName.fromSegments(listOf("kotlin", "native", "internal", "ExportTypeInfo"))
private fun exportTypeInfoIfRequired(irClass: IrClass, typeInfoGlobal: LLVMValueRef?) {
val annotation = irClass.annotations.findAnnotation(EXPORT_TYPE_INFO_FQ_NAME)
if (annotation != null) {
val name = annotation.getAnnotationStringValue()!!
// TODO: use LLVMAddAlias.
val global = addGlobal(name, pointerType(runtime.typeInfoType), isExported = true)
LLVMSetInitializer(global, typeInfoGlobal)
}
}
private val arrayClasses = mapOf(
IdSignatureValues.array to kObjHeaderPtr,
primitiveArrayTypesSignatures[PrimitiveType.BYTE] to int8Type,
primitiveArrayTypesSignatures[PrimitiveType.CHAR] to int16Type,
primitiveArrayTypesSignatures[PrimitiveType.SHORT] to int16Type,
primitiveArrayTypesSignatures[PrimitiveType.INT] to int32Type,
primitiveArrayTypesSignatures[PrimitiveType.LONG] to int64Type,
primitiveArrayTypesSignatures[PrimitiveType.FLOAT] to floatType,
primitiveArrayTypesSignatures[PrimitiveType.DOUBLE] to doubleType,
primitiveArrayTypesSignatures[PrimitiveType.BOOLEAN] to int8Type,
IdSignatureValues.string to int16Type,
getPublicSignature(KonanFqNames.packageName, "ImmutableBlob") to int8Type,
getPublicSignature(KonanFqNames.internalPackageName, "NativePtrArray") to kInt8Ptr
)
// Keep in sync with Konan_RuntimeType.
private val runtimeTypeMap = mapOf(
kObjHeaderPtr to 1,
int8Type to 2,
int16Type to 3,
int32Type to 4,
int64Type to 5,
floatType to 6,
doubleType to 7,
kInt8Ptr to 8,
int1Type to 9,
vector128Type to 10
)
private fun getElementType(irClass: IrClass): LLVMTypeRef? =
if (irClass.symbol.isPublicApi) arrayClasses[irClass.symbol.signature as IdSignature.PublicSignature] else null
private fun getInstanceSize(classType: LLVMTypeRef?, irClass: IrClass) : Int {
val elementType = getElementType(irClass)
// Check if it is an array.
if (elementType != null) return -LLVMABISizeOfType(llvmTargetData, elementType).toInt()
return LLVMStoreSizeOfType(llvmTargetData, classType).toInt()
}
private fun getClassId(irClass: IrClass): Int {
if (irClass.isKotlinObjCClass()) return 0
val hierarchyInfo = if (context.ghaEnabled()) {
context.getLayoutBuilder(irClass).hierarchyInfo
} else {
ClassGlobalHierarchyInfo.DUMMY
}
return if (irClass.isInterface) {
hierarchyInfo.interfaceId
} else {
hierarchyInfo.classIdLo
}
}
fun generate(irClass: IrClass) {
val className = irClass.fqNameForIrSerialization
val llvmDeclarations = context.llvmDeclarations.forClass(irClass)
val bodyType = llvmDeclarations.bodyType
val instanceSize = getInstanceSize(bodyType, irClass)
val superType = when {
irClass.isAny() -> NullPointer(runtime.typeInfoType)
irClass.isKotlinObjCClass() -> context.ir.symbols.any.owner.typeInfoPtr
else -> {
val superTypeOrAny = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner
superTypeOrAny.typeInfoPtr
}
}
val implementedInterfaces = irClass.implementedInterfaces.filter { it.requiresRtti() }
val interfaces = implementedInterfaces.map { it.typeInfoPtr }
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
pointerType(runtime.typeInfoType), interfaces)
val objOffsets = getObjOffsets(bodyType)
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type, objOffsets)
val objOffsetsCount = if (irClass.descriptor == context.builtIns.array) {
1 // To mark it as non-leaf.
} else {
objOffsets.size
}
val methods = if (irClass.isAbstract()) {
emptyList()
} else {
methodTableRecords(irClass)
}
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
runtime.methodTableRecordType, methods)
val needInterfaceTable = context.ghaEnabled() && !irClass.isInterface
&& !irClass.isAbstract() && !irClass.isObjCClass()
val (interfaceTable, interfaceTableSize) = if (needInterfaceTable) {
interfaceTableRecords(irClass)
} else {
Pair(emptyList(), -1)
}
val interfaceTablePtr = staticData.placeGlobalConstArray("kifacetable:$className",
runtime.interfaceTableRecordType, interfaceTable)
val reflectionInfo = getReflectionInfo(irClass)
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
val typeInfo = TypeInfo(
irClass.typeInfoPtr,
makeExtendedInfo(irClass),
instanceSize,
superType,
objOffsetsPtr, objOffsetsCount,
interfacesPtr, interfaces.size,
methodsPtr, methods.size,
interfaceTableSize, interfaceTablePtr,
reflectionInfo.packageName,
reflectionInfo.relativeName,
flagsFromClass(irClass),
getClassId(irClass),
llvmDeclarations.writableTypeInfoGlobal?.pointer,
associatedObjects = genAssociatedObjects(irClass)
)
val typeInfoGlobalValue = if (!irClass.typeInfoHasVtableAttached) {
typeInfo
} else {
val vtable = vtable(irClass)
Struct(typeInfo, vtable)
}
typeInfoGlobal.setInitializer(typeInfoGlobalValue)
typeInfoGlobal.setConstant(true)
exportTypeInfoIfRequired(irClass, irClass.llvmTypeInfoPtr)
}
private fun getObjOffsets(bodyType: LLVMTypeRef): List<Int32> =
getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}.map { Int32(it.toInt()) }
fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
val implementation = it.implementation
if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) {
NullPointer(int8Type)
} else {
implementation.entryPointAddress
}
}
return ConstArray(int8TypePtr, vtableEntries)
}
fun methodTableRecords(irClass: IrClass): List<MethodTableRecord> {
val functionNames = mutableMapOf<Long, OverriddenFunctionInfo>()
return context.getLayoutBuilder(irClass).methodTableEntries.map {
val functionName = it.overriddenFunction.computeFunctionName()
val nameSignature = functionName.localHash
val previous = functionNames.putIfAbsent(nameSignature.value, it)
if (previous != null)
throw AssertionError("Duplicate method table entry: functionName = '$functionName', hash = '${nameSignature.value}', entry1 = $previous, entry2 = $it")
// TODO: compile-time resolution limits binary compatibility.
val implementation = it.implementation
val methodEntryPoint =
if (implementation == null || context.referencedFunctions?.contains(implementation) == false)
null
else implementation.entryPointAddress
MethodTableRecord(nameSignature, methodEntryPoint)
}.sortedBy { it.nameSignature.value }
}
fun interfaceTableRecords(irClass: IrClass): Pair<List<InterfaceTableRecord>, Int> {
// The details are in ClassLayoutBuilder.
val interfaces = irClass.implementedInterfaces
val (interfaceTableSkeleton, interfaceTableSize) = interfaceTableSkeleton(interfaces)
val interfaceTableEntries = interfaceTableRecords(irClass, interfaceTableSkeleton)
return Pair(interfaceTableEntries, interfaceTableSize)
}
private fun interfaceTableSkeleton(interfaces: List<IrClass>): Pair<Array<out ClassLayoutBuilder?>, Int> {
val interfaceLayouts = interfaces.map { context.getLayoutBuilder(it) }
val interfaceColors = interfaceLayouts.map { it.hierarchyInfo.interfaceColor }
// Find the optimal size. It must be a power of 2.
var size = 1
val maxSize = 1 shl ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
val used = BooleanArray(maxSize)
while (size <= maxSize) {
for (i in 0 until size)
used[i] = false
// Check for collisions.
var ok = true
for (color in interfaceColors) {
val index = color % size
if (used[index]) {
ok = false
break
}
used[index] = true
}
if (ok) break
size *= 2
}
val conservative = size > maxSize
val interfaceTableSkeleton = if (conservative) {
size = interfaceLayouts.size
interfaceLayouts.sortedBy { it.hierarchyInfo.interfaceId }.toTypedArray()
} else arrayOfNulls<ClassLayoutBuilder?>(size).also {
for (interfaceLayout in interfaceLayouts)
it[interfaceLayout.hierarchyInfo.interfaceId % size] = interfaceLayout
}
val interfaceTableSize = if (conservative) -size else (size - 1)
return Pair(interfaceTableSkeleton, interfaceTableSize)
}
private fun interfaceTableRecords(
irClass: IrClass,
interfaceTableSkeleton: Array<out ClassLayoutBuilder?>
): List<InterfaceTableRecord> {
val methodTableEntries = context.getLayoutBuilder(irClass).methodTableEntries
val className = irClass.fqNameForIrSerialization
return interfaceTableSkeleton.map { iface ->
val interfaceId = iface?.hierarchyInfo?.interfaceId ?: 0
InterfaceTableRecord(
Int32(interfaceId),
Int32(iface?.interfaceTableEntries?.size ?: 0),
if (iface == null)
NullPointer(kInt8Ptr)
else {
val vtableEntries = iface.interfaceTableEntries.map { ifaceFunction ->
val impl = OverriddenFunctionInfo(
methodTableEntries.first { ifaceFunction in it.function.allOverriddenFunctions }.function,
ifaceFunction
).implementation
if (impl == null || context.referencedFunctions?.contains(impl) == false)
NullPointer(int8Type)
else impl.entryPointAddress
}
staticData.placeGlobalConstArray("kifacevtable:${className}_$interfaceId",
kInt8Ptr, vtableEntries
)
}
)
}
}
private fun mapRuntimeType(type: LLVMTypeRef): Int =
runtimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
private val debugRuntimeOrNull: LLVMModuleRef? by lazy {
context.config.runtimeNativeLibraries.singleOrNull { it.endsWith("debug.bc")}?.let {
parseBitcodeFile(it)
}
}
private val debugOperations: ConstValue by lazy {
if (debugRuntimeOrNull != null) {
val external = LLVMGetNamedGlobal(debugRuntimeOrNull, "Konan_debugOperationsList")!!
val local = LLVMAddGlobal(context.llvmModule, LLVMGetElementType(LLVMTypeOf(external)),"Konan_debugOperationsList")!!
constPointer(LLVMConstBitCast(local, kInt8PtrPtr)!!)
} else {
Zero(kInt8PtrPtr)
}
}
val debugOperationsSize: ConstValue by lazy {
if (debugRuntimeOrNull != null) {
val external = LLVMGetNamedGlobal(debugRuntimeOrNull, "Konan_debugOperationsList")!!
Int32(LLVMGetArrayLength(LLVMGetElementType(LLVMTypeOf(external))))
} else
Int32(0)
}
private fun makeExtendedInfo(irClass: IrClass): ConstPointer {
// TODO: shall we actually do that?
if (context.shouldOptimize())
return NullPointer(runtime.extendedTypeInfoType)
val className = irClass.fqNameForIrSerialization.toString()
val llvmDeclarations = context.llvmDeclarations.forClass(irClass)
val bodyType = llvmDeclarations.bodyType
val elementType = getElementType(irClass)
val value = if (elementType != null) {
// An array type.
val runtimeElementType = mapRuntimeType(elementType)
Struct(runtime.extendedTypeInfoType,
Int32(-runtimeElementType),
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr),
debugOperationsSize, debugOperations)
} else {
data class FieldRecord(val offset: Int, val type: Int, val name: String)
val fields = getStructElements(bodyType).drop(1).mapIndexed { index, type ->
FieldRecord(
LLVMOffsetOfElement(llvmTargetData, bodyType, index + 1).toInt(),
mapRuntimeType(type),
context.getLayoutBuilder(irClass).fields[index].name.asString())
}
val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", int32Type,
fields.map { Int32(it.offset) })
val typesPtr = staticData.placeGlobalConstArray("kexttype:$className", int8Type,
fields.map { Int8(it.type.toByte()) })
val namesPtr = staticData.placeGlobalConstArray("kextname:$className", kInt8Ptr,
fields.map { staticData.placeCStringLiteral(it.name) })
Struct(runtime.extendedTypeInfoType, Int32(fields.size), offsetsPtr, typesPtr, namesPtr,
debugOperationsSize, debugOperations)
}
val result = staticData.placeGlobal("", value)
result.setConstant(true)
return result.pointer
}
private fun genAssociatedObjects(irClass: IrClass): ConstPointer? {
val associatedObjects = context.getLayoutBuilder(irClass).associatedObjects
if (associatedObjects.isEmpty()) {
return null
}
val associatedObjectTableRecords = associatedObjects.map { (key, value) ->
val associatedObjectGetter = generateFunction(
CodeGenerator(context),
functionType(kObjHeaderPtr, false, kObjHeaderPtrPtr),
""
) {
ret(getObjectValue(value, ExceptionHandler.Caller, startLocationInfo = null))
}
Struct(runtime.associatedObjectTableRecordType, key.typeInfoPtr, constPointer(associatedObjectGetter))
}
return staticData.placeGlobalConstArray(
name = "kassociatedobjects:${irClass.fqNameForIrSerialization}",
elemType = runtime.associatedObjectTableRecordType,
elements = associatedObjectTableRecords + Struct(runtime.associatedObjectTableRecordType, null, null)
)
}
// TODO: extract more code common with generate().
fun generateSyntheticInterfaceImpl(
irClass: IrClass,
methodImpls: Map<IrFunction, ConstPointer>,
bodyType: LLVMTypeRef,
immutable: Boolean = false
): ConstPointer {
assert(irClass.isInterface)
val size = LLVMStoreSizeOfType(llvmTargetData, bodyType).toInt()
val superClass = context.ir.symbols.any.owner
assert(superClass.implementedInterfaces.isEmpty())
val interfaces = (listOf(irClass) + irClass.implementedInterfaces)
val interfacesPtr = staticData.placeGlobalConstArray("",
pointerType(runtime.typeInfoType), interfaces.map { it.typeInfoPtr })
assert(superClass.declarations.all { it !is IrProperty && it !is IrField })
val objOffsets = getObjOffsets(bodyType)
val objOffsetsPtr = staticData.placeGlobalConstArray("", int32Type, objOffsets)
val objOffsetsCount = objOffsets.size
val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
assert(method.parent == irClass)
MethodTableRecord(method.computeFunctionName().localHash, impl.bitcast(int8TypePtr))
}).sortedBy { it.nameSignature.value }.also {
assert(it.distinctBy { it.nameSignature.value } == it)
}
val methodsPtr = staticData.placeGlobalConstArray("", runtime.methodTableRecordType, methods)
val reflectionInfo = ReflectionInfo(null, null)
val writableTypeInfoType = runtime.writableTypeInfoType
val writableTypeInfo = if (writableTypeInfoType == null) {
null
} else {
staticData.createGlobal(writableTypeInfoType, "")
.also { it.setZeroInitializer() }
.pointer
}
val vtable = vtable(superClass)
val typeInfoWithVtableType = structType(runtime.typeInfoType, vtable.llvmType)
val typeInfoWithVtableGlobal = staticData.createGlobal(typeInfoWithVtableType, "", isExported = false)
val result = typeInfoWithVtableGlobal.pointer.getElementPtr(0)
val typeHierarchyInfo = if (!context.ghaEnabled())
ClassGlobalHierarchyInfo.DUMMY
else
ClassGlobalHierarchyInfo(-1, -1, 0, 0)
// TODO: interfaces (e.g. FunctionN and Function) should have different colors.
val (interfaceTableSkeleton, interfaceTableSize) =
if (context.ghaEnabled()) interfaceTableSkeleton(interfaces) else Pair(emptyArray(), -1)
val interfaceTable = interfaceTableSkeleton.map { layoutBuilder ->
if (layoutBuilder == null) {
InterfaceTableRecord(Int32(0), Int32(0), null)
} else {
val vtableEntries = layoutBuilder.interfaceTableEntries.map { methodImpls[it]!!.bitcast(int8TypePtr) }
val interfaceVTable = staticData.placeGlobalArray("", kInt8Ptr, vtableEntries)
InterfaceTableRecord(
Int32(layoutBuilder.hierarchyInfo.interfaceId),
Int32(layoutBuilder.interfaceTableEntries.size),
interfaceVTable.pointer.getElementPtr(0)
)
}
}
val interfaceTablePtr = staticData.placeGlobalConstArray("", runtime.interfaceTableRecordType, interfaceTable)
val typeInfoWithVtable = Struct(TypeInfo(
selfPtr = result,
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
size = size,
superType = superClass.typeInfoPtr,
objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount,
interfaces = interfacesPtr, interfacesCount = interfaces.size,
methods = methodsPtr, methodsCount = methods.size,
interfaceTableSize = interfaceTableSize, interfaceTable = interfaceTablePtr,
packageName = reflectionInfo.packageName,
relativeName = reflectionInfo.relativeName,
flags = flagsFromClass(irClass) or (if (immutable) TF_IMMUTABLE else 0),
classId = typeHierarchyInfo.classIdLo,
writableTypeInfo = writableTypeInfo,
associatedObjects = null
), vtable)
typeInfoWithVtableGlobal.setInitializer(typeInfoWithVtable)
typeInfoWithVtableGlobal.setConstant(true)
return result
}
private val OverriddenFunctionInfo.implementation get() = getImplementation(context)
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
private fun getReflectionInfo(irClass: IrClass): ReflectionInfo = when {
irClass.isAnonymousObject -> ReflectionInfo(packageName = null, relativeName = null)
irClass.isLocal -> ReflectionInfo(packageName = null, relativeName = irClass.name.asString())
else -> ReflectionInfo(
packageName = irClass.findPackage().fqNameForIrSerialization.asString(),
relativeName = generateSequence(irClass) { it.parent as? IrClass }
.toList().reversed()
.joinToString(".") { it.name.asString() }
)
}
fun dispose() {
debugRuntimeOrNull?.let { LLVMDisposeModule(it) }
}
}
// Keep in sync with Konan_TypeFlags in TypeInfo.h.
private const val TF_IMMUTABLE = 1
private const val TF_ACYCLIC = 2
private const val TF_INTERFACE = 4
private const val TF_OBJC_DYNAMIC = 8
private const val TF_LEAK_DETECTOR_CANDIDATE = 16
private const val TF_SUSPEND_FUNCTION = 32
private const val TF_HAS_FINALIZER = 64
private const val TF_HAS_FREEZE_HOOK = 128
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.FqName
private val retainAnnotationName = FqName("kotlin.native.Retain")
private val retainForTargetAnnotationName = FqName("kotlin.native.RetainForTarget")
internal fun IrFunction.retainAnnotation(target: KonanTarget): Boolean {
if (this.annotations.findAnnotation(retainAnnotationName) != null) return true
val forTarget = this.annotations.findAnnotation(retainForTargetAnnotationName)
if (forTarget != null && forTarget.getAnnotationStringValue() == target.name) return true
return false
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.declarations.IrFunction
interface RuntimeAware {
val runtime: Runtime
}
class Runtime(bitcodeFile: String) {
val llvmModule: LLVMModuleRef = parseBitcodeFile(bitcodeFile)
val calculatedLLVMTypes: MutableMap<IrType, LLVMTypeRef> = HashMap()
val addedLLVMExternalFunctions: MutableMap<IrFunction, LLVMValueRef> = HashMap()
internal fun getStructTypeOrNull(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name")
internal fun getStructType(name: String) = getStructTypeOrNull(name)
?: throw Error("struct.$name is not found in the Runtime module.")
val typeInfoType = getStructType("TypeInfo")
val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
val methodTableRecordType = getStructType("MethodTableRecord")
val interfaceTableRecordType = getStructType("InterfaceTableRecord")
val globalHashType = getStructType("GlobalHash")
val associatedObjectTableRecordType = getStructType("AssociatedObjectTableRecord")
val objHeaderType = getStructType("ObjHeader")
val objHeaderPtrType = pointerType(objHeaderType)
val objHeaderPtrPtrType = pointerType(objHeaderType)
val arrayHeaderType = getStructType("ArrayHeader")
val frameOverlayType = getStructType("FrameOverlay")
val target = LLVMGetTarget(llvmModule)!!.toKString()
val dataLayout = LLVMGetDataLayout(llvmModule)!!.toKString()
val targetData = LLVMCreateTargetData(dataLayout)!!
val kotlinObjCClassData by lazy { getStructType("KotlinObjCClassData") }
val kotlinObjCClassInfo by lazy { getStructType("KotlinObjCClassInfo") }
val objCMethodDescription by lazy { getStructType("ObjCMethodDescription") }
val objCTypeAdapter by lazy { getStructType("ObjCTypeAdapter") }
val objCToKotlinMethodAdapter by lazy { getStructType("ObjCToKotlinMethodAdapter") }
val kotlinToObjCMethodAdapter by lazy { getStructType("KotlinToObjCMethodAdapter") }
val typeInfoObjCExportAddition by lazy { getStructType("TypeInfoObjCExportAddition") }
val pointerSize: Int by lazy {
LLVMABISizeOfType(targetData, objHeaderPtrType).toInt()
}
val pointerAlignment: Int by lazy {
LLVMABIAlignmentOfType(targetData, objHeaderPtrType)
}
}
@@ -0,0 +1,178 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.expressions.IrConst
/**
* Provides utilities to create static data.
*/
internal class StaticData(override val context: Context): ContextUtils {
/**
* Represents the LLVM global variable.
*/
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) {
companion object {
private fun createLlvmGlobal(module: LLVMModuleRef,
type: LLVMTypeRef,
name: String,
isExported: Boolean
): LLVMValueRef {
if (isExported && LLVMGetNamedGlobal(module, name) != null) {
throw IllegalArgumentException("Global '$name' already exists")
}
// Globals created with this API are *not* thread local.
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
if (!isExported) {
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMInternalLinkage)
}
return llvmGlobal
}
fun create(staticData: StaticData, type: LLVMTypeRef, name: String, isExported: Boolean): Global {
val module = staticData.context.llvmModule
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
if (isUnnamed && isExported) {
throw IllegalArgumentException("unnamed global can't be exported")
}
val llvmGlobal = createLlvmGlobal(module!!, type, name, isExported)
return Global(staticData, llvmGlobal)
}
}
val type get() = getGlobalType(this.llvmGlobal)
fun setInitializer(value: ConstValue) {
LLVMSetInitializer(llvmGlobal, value.llvm)
}
fun setZeroInitializer() {
LLVMSetInitializer(llvmGlobal, LLVMConstNull(this.type)!!)
}
fun setConstant(value: Boolean) {
LLVMSetGlobalConstant(llvmGlobal, if (value) 1 else 0)
}
fun setLinkage(value: LLVMLinkage) {
LLVMSetLinkage(llvmGlobal, value)
}
fun setAlignment(value: Int) {
LLVMSetAlignment(llvmGlobal, value)
}
fun setSection(name: String) {
LLVMSetSection(llvmGlobal, name)
}
val pointer = Pointer.to(this)
}
/**
* Represents the pointer to static data.
* It can be a pointer to either a global or any its element.
*
* TODO: this class is probably should be implemented more optimally
*/
class Pointer private constructor(val global: Global,
private val delegate: ConstPointer,
val offsetInGlobal: Long) : ConstPointer by delegate {
companion object {
fun to(global: Global) = Pointer(global, constPointer(global.llvmGlobal), 0L)
}
private fun getElementOffset(index: Int): Long {
val llvmTargetData = global.staticData.llvmTargetData
val type = LLVMGetElementType(delegate.llvmType)
return when (LLVMGetTypeKind(type)) {
LLVMTypeKind.LLVMStructTypeKind -> LLVMOffsetOfElement(llvmTargetData, type, index)
LLVMTypeKind.LLVMArrayTypeKind -> LLVMABISizeOfType(llvmTargetData, LLVMGetElementType(type)) * index
else -> TODO()
}
}
override fun getElementPtr(index: Int): Pointer {
return Pointer(global, delegate.getElementPtr(index), offsetInGlobal + this.getElementOffset(index))
}
/**
* @return the distance from other pointer to this.
*
* @throws UnsupportedOperationException if it is not possible to represent the distance as [Int] value
*/
fun sub(other: Pointer): Int {
if (this.global != other.global) {
throw UnsupportedOperationException("pointers must belong to the same global")
}
val res = this.offsetInGlobal - other.offsetInGlobal
if (res.toInt().toLong() != res) {
throw UnsupportedOperationException("result doesn't fit into Int")
}
return res.toInt()
}
}
/**
* Creates [Global] with given type and name.
*
* It is external until explicitly initialized with [Global.setInitializer].
*/
fun createGlobal(type: LLVMTypeRef, name: String, isExported: Boolean = false): Global {
return Global.create(this, type, name, isExported)
}
/**
* Creates [Global] with given name and value.
*/
fun placeGlobal(name: String, initializer: ConstValue, isExported: Boolean = false): Global {
val global = createGlobal(initializer.llvmType, name, isExported)
global.setInitializer(initializer)
return global
}
/**
* Creates array-typed global with given name and value.
*/
fun placeGlobalArray(name: String, elemType: LLVMTypeRef?, elements: List<ConstValue>, isExported: Boolean = false): Global {
val initializer = ConstArray(elemType, elements)
val global = placeGlobal(name, initializer, isExported)
return global
}
private val stringLiterals = mutableMapOf<String, ConstPointer>()
private val cStringLiterals = mutableMapOf<String, ConstPointer>()
fun cStringLiteral(value: String) =
cStringLiterals.getOrPut(value) { placeCStringLiteral(value) }
fun kotlinStringLiteral(value: String) =
stringLiterals.getOrPut(value) { createKotlinStringLiteral(value) }
}
/**
* Creates static instance of `konan.ImmutableByteArray` with given values of elements.
*
* @param args data for constant creation.
*/
internal fun StaticData.createImmutableBlob(value: IrConst<String>): LLVMValueRef {
val args = value.value.map { Int8(it.toByte()).llvm }
return createConstKotlinArray(context.ir.symbols.immutableBlob.owner, args)
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
/**
* Creates const array-typed global with given name and value.
* Returns pointer to the first element of the array.
*
* If [elements] is empty, then null pointer is returned.
*/
internal fun StaticData.placeGlobalConstArray(name: String,
elemType: LLVMTypeRef,
elements: List<ConstValue>,
isExported: Boolean = false): ConstPointer {
if (elements.isNotEmpty() || isExported) {
val global = this.placeGlobalArray(name, elemType, elements, isExported)
global.setConstant(true)
return global.pointer.getElementPtr(0)
} else {
return NullPointer(elemType)
}
}
internal fun StaticData.createAlias(name: String, aliasee: ConstPointer): ConstPointer {
val alias = LLVMAddAlias(context.llvmModule, aliasee.llvmType, aliasee.llvm, name)!!
return constPointer(alias)
}
internal fun StaticData.placeCStringLiteral(value: String): ConstPointer {
val chars = value.toByteArray(Charsets.UTF_8).map { Int8(it) } + Int8(0)
return placeGlobalConstArray("", int8Type, chars)
}
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.cValuesOf
import llvm.*
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.ir.declarations.IrClass
private fun ConstPointer.add(index: Int): ConstPointer {
return constPointer(LLVMConstGEP(llvm, cValuesOf(Int32(index).llvm), 1)!!)
}
// Must match OBJECT_TAG_PERMANENT_CONTAINER in C++.
private fun StaticData.permanentTag(typeInfo: ConstPointer): ConstPointer {
// Only pointer arithmetic via GEP works on constant pointers in LLVM.
return typeInfo.bitcast(int8TypePtr).add(1).bitcast(kTypeInfoPtr)
}
private fun StaticData.objHeader(typeInfo: ConstPointer): Struct {
return Struct(runtime.objHeaderType, permanentTag(typeInfo))
}
private fun StaticData.arrayHeader(typeInfo: ConstPointer, length: Int): Struct {
assert (length >= 0)
return Struct(runtime.arrayHeaderType, permanentTag(typeInfo), Int32(length))
}
internal fun StaticData.createKotlinStringLiteral(value: String): ConstPointer {
val elements = value.toCharArray().map(::Char16)
val objRef = createConstKotlinArray(context.ir.symbols.string.owner, elements)
return objRef
}
private fun StaticData.createRef(objHeaderPtr: ConstPointer) = objHeaderPtr.bitcast(kObjHeaderPtr)
internal fun StaticData.createConstKotlinArray(arrayClass: IrClass, elements: List<LLVMValueRef>) =
createConstKotlinArray(arrayClass, elements.map { constValue(it) }).llvm
internal fun StaticData.createConstKotlinArray(arrayClass: IrClass, elements: List<ConstValue>): ConstPointer {
val typeInfo = arrayClass.typeInfoPtr
val bodyElementType: LLVMTypeRef = elements.firstOrNull()?.llvmType ?: int8Type
// (use [0 x i8] as body if there are no elements)
val arrayBody = ConstArray(bodyElementType, elements)
val compositeType = structType(runtime.arrayHeaderType, arrayBody.llvmType)
val global = this.createGlobal(compositeType, "")
val objHeaderPtr = global.pointer.getElementPtr(0)
val arrayHeader = arrayHeader(typeInfo, elements.size)
global.setInitializer(Struct(compositeType, arrayHeader, arrayBody))
global.setConstant(true)
return createRef(objHeaderPtr)
}
internal fun StaticData.createConstKotlinObject(type: IrClass, vararg fields: ConstValue): ConstPointer {
val typeInfo = type.typeInfoPtr
val objHeader = objHeader(typeInfo)
val global = this.placeGlobal("", Struct(objHeader, *fields))
global.setConstant(true)
val objHeaderPtr = global.pointer.getElementPtr(0)
return createRef(objHeaderPtr)
}
internal fun StaticData.createInitializer(type: IrClass, vararg fields: ConstValue): ConstValue =
Struct(objHeader(type.typeInfoPtr), *fields)
/**
* Creates static instance of `kotlin.collections.ArrayList<elementType>` with given values of fields.
*
* @param array value for `array: Array<E>` field.
* @param length value for `length: Int` field.
*/
internal fun StaticData.createConstArrayList(array: ConstPointer, length: Int): ConstPointer {
val arrayListClass = context.ir.symbols.arrayList.owner
val arrayListFields = mapOf(
"array" to array,
"offset" to Int32(0),
"length" to Int32(length),
"backing" to NullPointer(kObjHeader))
// Now sort these values according to the order of fields returned by getFields()
// to match the sorting order of the real ArrayList().
val sorted = mutableListOf<ConstValue>()
context.getLayoutBuilder(arrayListClass).fields.forEach {
require (it.parent == arrayListClass)
sorted.add(arrayListFields[it.name.asString()]!!)
}
return createConstKotlinObject(arrayListClass, *sorted.toTypedArray())
}
internal fun StaticData.createUniqueInstance(
kind: UniqueKind, bodyType: LLVMTypeRef, typeInfo: ConstPointer): ConstPointer {
assert (getStructElements(bodyType).size == 1) // ObjHeader only.
val objHeader = when (kind) {
UniqueKind.UNIT -> objHeader(typeInfo)
UniqueKind.EMPTY_ARRAY -> arrayHeader(typeInfo, 0)
}
val global = this.placeGlobal(kind.llvmName, objHeader, isExported = true)
global.setConstant(true)
return global.pointer
}
internal fun ContextUtils.unique(kind: UniqueKind): ConstPointer {
val descriptor = when (kind) {
UniqueKind.UNIT -> context.ir.symbols.unit.owner
UniqueKind.EMPTY_ARRAY -> context.ir.symbols.array.owner
}
return if (isExternal(descriptor)) {
constPointer(importGlobal(
kind.llvmName, context.llvm.runtime.objHeaderType, origin = descriptor.llvmSymbolOrigin
))
} else {
context.llvmDeclarations.forUnique(kind).pointer
}
}
internal val ContextUtils.theUnitInstanceRef: ConstPointer
get() = this.unique(UniqueKind.UNIT)

Some files were not shown because too many files have changed in this diff Show More