Skip to content

Commit

Permalink
Use clang instead if llvm-lto for darwin-based targets (JetBrains#2974)
Browse files Browse the repository at this point in the history
* Use LLVM C API for optimizations
* Use Clang for bitcode compilation
* Test bitcode embedding
  • Loading branch information
sbogolepov authored May 15, 2019
1 parent 89c2fb2 commit 5306d7b
Show file tree
Hide file tree
Showing 23 changed files with 620 additions and 367 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(VERBOSE_PHASES,
arguments.verbosePhases.toNonNullList())
put(LIST_PHASES, arguments.listPhases)
put(TIME_PHASES, arguments.timePhases)

put(COMPATIBLE_COMPILER_VERSIONS,
arguments.compatibleCompilerVersions.toNonNullList())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@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 = "-Xtime", deprecatedName = "--time", description = "Report execution time for compiler phases")
var timePhases: Boolean = false

@Argument(value = "-Xverify-bitcode", deprecatedName = "--verify_bitcode", description = "Verify llvm bitcode after each method")
var verifyBitCode: Boolean = false

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* 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.llvm.parseBitcodeFile
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.target.*

typealias BitcodeFile = String
typealias ObjectFile = String
typealias ExecutableFile = String

private fun mangleSymbol(target: KonanTarget,symbol: String) =
if (target.family == Family.IOS || target.family == Family.OSX) {
"_$symbol"
} else {
symbol
}

internal class BitcodeCompiler(val context: Context) {

private val target = context.config.target
private val platform = context.config.platform
private val optimize = context.shouldOptimize()
private val debug = context.config.debug

private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { !it.isEmpty() })
}

private val exportedSymbols = context.coverage.addExportedSymbols()

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 llvmLto(configurables: LlvmLtoFlags, file: BitcodeFile): ObjectFile {
val combined = temporary("combined", ".o")
val arguments = mutableListOf<String>().apply {
addNonEmpty(configurables.llvmLtoFlags)
addNonEmpty(llvmProfilingFlags())
when {
optimize -> addNonEmpty(configurables.llvmLtoOptFlags)
debug -> addNonEmpty(platform.llvmDebugOptFlags)
else -> addNonEmpty(configurables.llvmLtoNooptFlags)
}
addNonEmpty(configurables.llvmLtoDynamicFlags)
add(file)
// Prevent symbols from being deleted by DCE.
addNonEmpty(exportedSymbols.map { "-exported-symbol=${mangleSymbol(target, it)}"} )
}
hostLlvmTool("llvm-lto", "-o", combined, *arguments.toTypedArray())
return combined
}

private fun opt(optFlags: OptFlags, bitcodeFile: BitcodeFile): BitcodeFile {
val flags = (optFlags.optFlags + when {
optimize -> optFlags.optOptFlags
debug -> optFlags.optDebugFlags
else -> optFlags.optNooptFlags
} + llvmProfilingFlags()).toTypedArray()
val optimizedBc = temporary("opt_output", ".bc")
hostLlvmTool("opt", bitcodeFile, "-o", optimizedBc, *flags)

if (shouldRunLateBitcodePasses(context)) {
val module = parseBitcodeFile(optimizedBc)
runLateBitcodePasses(context, module)
LLVMWriteBitcodeToFile(module, optimizedBc)
}

return optimizedBc
}

private fun llc(llcFlags: LlcFlags, bitcodeFile: BitcodeFile): ObjectFile {
val flags = (llcFlags.llcFlags + when {
optimize -> llcFlags.llcOptFlags
debug -> llcFlags.llcDebugFlags
else -> llcFlags.llcNooptFlags
} + llvmProfilingFlags()).toTypedArray()
val combinedO = temporary("llc_output", ".o")
hostLlvmTool("llc", bitcodeFile, "-o", combinedO, *flags, "-filetype=obj")
return combinedO
}

private fun bitcodeToWasm(configurables: WasmConfigurables, file: BitcodeFile): String {
val optimizedBc = opt(configurables, file)
val compiled = llc(configurables, optimizedBc)

// TODO: should be moved to linker.
val linkedWasm = temporary("linked", ".wasm")
hostLlvmTool("wasm-ld", compiled, "-o", linkedWasm, *configurables.lldFlags.toTypedArray())

return linkedWasm
}

private fun optAndLlc(configurables: ZephyrConfigurables, file: BitcodeFile): String {
val optimizedBc = temporary("optimized", ".bc")
val optFlags = llvmProfilingFlags() + listOf("-O3", "-internalize", "-globaldce")
hostLlvmTool("opt", file, "-o=$optimizedBc", *optFlags.toTypedArray())

val combinedO = temporary("combined", ".o")
val llcFlags = llvmProfilingFlags() + listOf("-function-sections", "-data-sections")
hostLlvmTool("llc", optimizedBc, "-filetype=obj", "-o", combinedO, *llcFlags.toTypedArray())

return combinedO
}

private fun clang(configurables: AppleConfigurables, file: BitcodeFile): ObjectFile {
val objectFile = temporary("result", ".o")

val profilingFlags = llvmProfilingFlags().map { listOf("-mllvm", it) }.flatten()

val flags = mutableListOf<String>().apply {
addNonEmpty(configurables.clangFlags)
addNonEmpty(listOf("-triple", context.llvm.targetTriple))
addNonEmpty(when {
optimize -> configurables.clangOptFlags
debug -> configurables.clangDebugFlags
else -> configurables.clangNooptFlags
})
addNonEmpty(BitcodeEmbedding.getClangOptions(context.config))
if (determineLinkerOutput(context) == LinkerOutputKind.DYNAMIC_LIBRARY) {
addNonEmpty(configurables.clangDynamicFlags)
}
addNonEmpty(profilingFlags)
}
targetTool("clang++", *flags.toTypedArray(), file, "-o", objectFile)
return objectFile
}

// llvm-lto, opt and llc share same profiling flags, so we can
// reuse this function.
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 AppleConfigurables ->
clang(configurables, bitcodeFile)
is WasmConfigurables ->
bitcodeToWasm(configurables, bitcodeFile)
is ZephyrConfigurables ->
optAndLlc(configurables, bitcodeFile)
is LlvmLtoFlags ->
llvmLto(configurables, bitcodeFile)
else ->
error("Unsupported configurables kind: ${configurables::class.simpleName}!")
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ object BitcodeEmbedding {
Mode.FULL -> listOf("-bitcode_bundle")
}

internal fun getClangOptions(config: KonanConfig): List<String> = when (config.bitcodeEmbeddingMode) {
BitcodeEmbedding.Mode.NONE -> listOf("-fembed-bitcode=off")
BitcodeEmbedding.Mode.MARKER -> listOf("-fembed-bitcode=marker")
BitcodeEmbedding.Mode.FULL -> listOf("-fembed-bitcode=all")
}

private val KonanConfig.bitcodeEmbeddingMode get() = configuration.get(KonanConfigKeys.BITCODE_EMBEDDING_MODE)!!.also {
require(it == Mode.NONE || this.produce == CompilerOutputKind.FRAMEWORK) {
"${it.name.toLowerCase()} bitcode embedding mode is not supported when producing ${this.produce.name.toLowerCase()}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
*/
package org.jetbrains.kotlin.backend.konan

import llvm.*
import llvm.LLVMLinkModules2
import llvm.LLVMModuleRef
import llvm.LLVMWriteBitcodeToFile
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.llvm.embedLlvmLinkOptions
import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile
import org.jetbrains.kotlin.konan.CURRENT
import org.jetbrains.kotlin.konan.KonanAbiVersion
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.file.isBitcode
import org.jetbrains.kotlin.konan.library.KonanLibraryVersioning
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Family

val CompilerOutputKind.isNativeBinary: Boolean get() = when (this) {
CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC,
Expand All @@ -28,20 +31,30 @@ internal fun produceCStubs(context: Context) {
}
}

private fun shouldRunBitcodePasses(context: Context): Boolean =
context.coverage.enabled
private fun linkAllDependecies(context: Context, generatedBitcodeFiles: List<String>) {

val nativeLibraries = context.config.nativeLibraries + context.config.defaultNativeLibraries
val bitcodeLibraries = context.llvm.bitcodeToLink.map { it.bitcodePaths }.flatten().filter { it.isBitcode }
val additionalBitcodeFilesToLink = context.llvm.additionalProducedBitcodeFiles
val bitcodeFiles = (nativeLibraries + generatedBitcodeFiles + additionalBitcodeFilesToLink + bitcodeLibraries).toSet()

internal fun runBitcodePasses(context: Context) {
if (!shouldRunBitcodePasses(context)) {
return
}
val llvmModule = context.llvmModule!!
val passManager = LLVMCreatePassManager()!!
val targetLibraryInfo = LLVMGetTargetLibraryInfo(llvmModule)
LLVMAddTargetLibraryInfo(targetLibraryInfo, passManager)
context.coverage.addLlvmPasses(passManager)
LLVMRunPassManager(passManager, llvmModule)
LLVMDisposePassManager(passManager)
bitcodeFiles.forEach {
parseAndLinkBitcodeFile(llvmModule, it)
}
}

private fun shouldOptimizeWithLlvmApi(context: Context) =
(context.config.target.family == Family.IOS || context.config.target.family == Family.OSX)

private fun shoudRunClosedWorldCleanUp(context: Context) =
// GlobalDCE will kill coverage-related globals.
!context.coverage.enabled

private fun runLlvmPipeline(context: Context) = when {
shouldOptimizeWithLlvmApi(context) -> runLlvmOptimizationPipeline(context)
shoudRunClosedWorldCleanUp(context) -> runClosedWorldCleanup(context)
else -> {}
}

internal fun produceOutput(context: Context) {
Expand All @@ -57,7 +70,6 @@ internal fun produceOutput(context: Context) {
CompilerOutputKind.PROGRAM -> {
val output = tempFiles.nativeBinaryFileName
context.bitcodeFileName = output

val generatedBitcodeFiles =
if (produce == CompilerOutputKind.DYNAMIC || produce == CompilerOutputKind.STATIC) {
produceCAdapterBitcode(
Expand All @@ -66,20 +78,11 @@ internal fun produceOutput(context: Context) {
tempFiles.cAdapterBitcodeName)
listOf(tempFiles.cAdapterBitcodeName)
} else emptyList()

val nativeLibraries =
context.config.nativeLibraries +
context.config.defaultNativeLibraries +
generatedBitcodeFiles

for (library in nativeLibraries) {
parseAndLinkBitcodeFile(context.llvmModule!!, library)
}

if (produce == CompilerOutputKind.FRAMEWORK && context.config.produceStaticFramework) {
embedAppleLinkerOptionsToBitcode(context.llvm, context.config)
}

linkAllDependecies(context, generatedBitcodeFiles)
runLlvmPipeline(context)
LLVMWriteBitcodeToFile(context.llvmModule!!, output)
}
CompilerOutputKind.LIBRARY -> {
Expand All @@ -94,7 +97,6 @@ internal fun produceOutput(context: Context) {
val nopack = config.getBoolean(KonanConfigKeys.NOPACK)
val manifestProperties = context.config.manifestProperties


val library = buildLibrary(
context.config.nativeLibraries,
context.config.includeBinaries,
Expand All @@ -109,7 +111,6 @@ internal fun produceOutput(context: Context) {
manifestProperties,
context.dataFlowGraph)

context.library = library
context.bitcodeFileName = library.mainBitcodeFileName
}
CompilerOutputKind.BITCODE -> {
Expand Down Expand Up @@ -147,4 +148,4 @@ private fun embedAppleLinkerOptionsToBitcode(llvm: Llvm, config: KonanConfig) {
llvm.nativeDependenciesToLink.flatMap { findEmbeddableOptions(it.linkerOpts) }

embedLlvmLinkOptions(llvm.llvmModule, optionsToEmbed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.common.descriptors.WrappedTypeParameterDescr
import org.jetbrains.kotlin.backend.common.validateIrModule
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryWriter
import org.jetbrains.kotlin.backend.konan.library.LinkData
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD
Expand All @@ -40,7 +39,6 @@ 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.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
Expand All @@ -54,6 +52,7 @@ import org.jetbrains.kotlin.backend.common.serialization.KotlinMangler
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.name.FqName

/**
* Offset for synthetic elements created by lowerings and not attributable to other places in the source code.
Expand Down Expand Up @@ -322,7 +321,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
val llvmImports: LlvmImports = Llvm.ImportsImpl(this)
lateinit var llvmDeclarations: LlvmDeclarations
lateinit var bitcodeFileName: String
lateinit var library: KonanLibraryWriter

val cStubsManager = CStubsManager(config.target)

Expand Down Expand Up @@ -441,7 +439,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {

fun shouldPrintLocations() = config.configuration.getBoolean(KonanConfigKeys.PRINT_LOCATIONS)

fun shouldProfilePhases() = config.configuration.getBoolean(KonanConfigKeys.TIME_PHASES)
fun shouldProfilePhases() = config.phaseConfig.needProfiling

fun shouldContainDebugInfo() = config.debug

Expand Down Expand Up @@ -469,7 +467,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
internal val stdlibModule
get() = this.builtIns.any.module

lateinit var linkStage: LinkStage
lateinit var compilerOutput: List<ObjectFile>
}

private fun MemberScope.getContributedClassifier(name: String) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("target we compile for")
val TEMPORARY_FILES_DIR: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("directory for temporary files")
val TIME_PHASES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("time backend phases")
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify bitcode")
val VERIFY_DESCRIPTORS: CompilerConfigurationKey<Boolean>
Expand Down
Loading

0 comments on commit 5306d7b

Please sign in to comment.