[MERGE] Kotlin/Native history merged into kotlin/master
This commit is contained in:
@@ -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>
|
||||
@@ -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)
|
||||
+321
@@ -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"
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.backend.konan.ObjCOverridabilityCondition
|
||||
+272
@@ -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?
|
||||
|
||||
}
|
||||
|
||||
+20
@@ -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
|
||||
}
|
||||
+116
@@ -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}!")
|
||||
})
|
||||
}
|
||||
+22
@@ -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)!!
|
||||
}
|
||||
+221
@@ -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
|
||||
}
|
||||
+354
@@ -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) }
|
||||
}
|
||||
}
|
||||
+15
@@ -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()
|
||||
}
|
||||
+1109
File diff suppressed because it is too large
Load Diff
+94
@@ -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
|
||||
}
|
||||
+131
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -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"
|
||||
}
|
||||
}
|
||||
+34
@@ -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()
|
||||
+211
@@ -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)
|
||||
}
|
||||
+474
@@ -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() }
|
||||
}
|
||||
+11
@@ -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),
|
||||
}
|
||||
+92
@@ -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
|
||||
}
|
||||
+219
@@ -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)
|
||||
}
|
||||
}
|
||||
+23
@@ -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)
|
||||
|
||||
+187
@@ -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
|
||||
}
|
||||
+89
@@ -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
|
||||
}
|
||||
}
|
||||
+319
@@ -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
|
||||
}
|
||||
+88
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
@@ -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)
|
||||
+88
@@ -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.
|
||||
)
|
||||
}
|
||||
+53
@@ -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)
|
||||
+205
@@ -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)
|
||||
+153
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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")
|
||||
}
|
||||
+86
@@ -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)
|
||||
}
|
||||
+388
@@ -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"
|
||||
)
|
||||
+63
@@ -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()
|
||||
}
|
||||
+238
@@ -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)
|
||||
}
|
||||
+24
@@ -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
|
||||
}
|
||||
+45
@@ -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
|
||||
}
|
||||
+11
@@ -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,
|
||||
}
|
||||
+335
@@ -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")
|
||||
+189
@@ -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
|
||||
}
|
||||
+86
@@ -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"
|
||||
}
|
||||
+215
@@ -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
|
||||
}
|
||||
+56
@@ -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)
|
||||
}
|
||||
}
|
||||
+23
@@ -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")
|
||||
}
|
||||
+12
@@ -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
|
||||
}
|
||||
+135
@@ -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())
|
||||
+488
@@ -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)
|
||||
}
|
||||
}
|
||||
+1391
File diff suppressed because it is too large
Load Diff
+251
@@ -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(')')
|
||||
}
|
||||
}
|
||||
+66
@@ -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(')')
|
||||
})
|
||||
}
|
||||
+118
@@ -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)
|
||||
|
||||
+466
@@ -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 }
|
||||
}
|
||||
+75
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+123
@@ -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
|
||||
}
|
||||
|
||||
+308
@@ -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()
|
||||
+98
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
+160
@@ -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)
|
||||
+65
@@ -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()
|
||||
+48
@@ -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))
|
||||
}
|
||||
}
|
||||
+25
@@ -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()
|
||||
|
||||
+576
@@ -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
|
||||
}
|
||||
+64
@@ -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())
|
||||
+49
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+132
@@ -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
|
||||
+189
@@ -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) }
|
||||
+119
@@ -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"
|
||||
}
|
||||
}
|
||||
+100
@@ -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
|
||||
}
|
||||
}
|
||||
+178
@@ -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
|
||||
}
|
||||
}
|
||||
+100
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+153
@@ -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
|
||||
+325
@@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
+1504
File diff suppressed because it is too large
Load Diff
+638
@@ -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)
|
||||
+43
@@ -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)
|
||||
}
|
||||
}
|
||||
+292
@@ -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)
|
||||
}
|
||||
+309
@@ -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)
|
||||
}
|
||||
+78
@@ -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()
|
||||
+53
@@ -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)
|
||||
+35
@@ -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
|
||||
+725
@@ -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")
|
||||
}
|
||||
}
|
||||
+2603
File diff suppressed because it is too large
Load Diff
+172
@@ -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
|
||||
}
|
||||
}
|
||||
+72
@@ -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", "")
|
||||
}
|
||||
+416
@@ -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
|
||||
}
|
||||
+16
@@ -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)
|
||||
}
|
||||
}
|
||||
+440
@@ -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
|
||||
}
|
||||
+646
@@ -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
|
||||
|
||||
+22
@@ -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
|
||||
}
|
||||
+62
@@ -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)
|
||||
}
|
||||
}
|
||||
+178
@@ -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)
|
||||
}
|
||||
+38
@@ -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)
|
||||
}
|
||||
+131
@@ -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
Reference in New Issue
Block a user