[K/N] Enable proper LLVM IR dump and verify actions
Also introduce -Xsave-llvm-ir-directory argument that should be used instead of -Xtemporary-files-dir as a location for LLVM IR from phases. Motivation for this change: it is simpler to implement and unties LLVM actions from the awful TempFiles class.
This commit is contained in:
committed by
Space Team
parent
0febffedbd
commit
851d84a865
+4
-1
@@ -279,7 +279,7 @@ 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 = "-Xsave-llvm-ir-after", description = "Save result of Kotlin IR to LLVM IR translation to the temporary files directory.")
|
||||
@Argument(value = "-Xsave-llvm-ir-after", description = "Save result of Kotlin IR to LLVM IR translation to -Xsave-llvm-ir-directory.")
|
||||
var saveLlvmIrAfter: Array<String> = emptyArray()
|
||||
|
||||
@Argument(value = "-Xverify-bitcode", deprecatedName = "--verify_bitcode", description = "Verify llvm bitcode after each method")
|
||||
@@ -408,6 +408,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xomit-framework-binary", description = "Omit binary when compiling framework")
|
||||
var omitFrameworkBinary: Boolean = false
|
||||
|
||||
@Argument(value = "-Xsave-llvm-ir-directory", description = "Directory that should contain results of -Xsave-llvm-ir-after=<phase>")
|
||||
var saveLlvmIrDirectory: String? = null
|
||||
|
||||
override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> =
|
||||
super.configureAnalysisFlags(collector, languageVersion).also {
|
||||
val optInList = it[AnalysisFlags.optIn] as List<*>
|
||||
|
||||
+16
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import com.google.common.io.Files
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.KonanUserVisibleIrModulesSupport
|
||||
@@ -13,7 +14,6 @@ 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
|
||||
@@ -489,6 +489,21 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory to store LLVM IR from -Xsave-llvm-ir-after.
|
||||
*/
|
||||
internal val saveLlvmIrDirectory: java.io.File by lazy {
|
||||
val path = configuration.get(KonanConfigKeys.SAVE_LLVM_IR_DIRECTORY)
|
||||
if (path == null) {
|
||||
val tempDir = Files.createTempDir()
|
||||
configuration.report(CompilerMessageSeverity.WARNING,
|
||||
"Temporary directory for LLVM IR is ${tempDir.canonicalPath}")
|
||||
tempDir
|
||||
} else {
|
||||
java.io.File(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String)
|
||||
|
||||
+1
@@ -161,6 +161,7 @@ class KonanConfigKeys {
|
||||
val PARTIAL_LINKAGE: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create("allows some symbols in klibs be missed")
|
||||
val TEST_DUMP_OUTPUT_PATH: CompilerConfigurationKey<String?> = CompilerConfigurationKey.create("path to a file to dump the list of all available tests")
|
||||
val OMIT_FRAMEWORK_BINARY: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create("do not generate binary in framework")
|
||||
val SAVE_LLVM_IR_DIRECTORY: CompilerConfigurationKey<String?> = CompilerConfigurationKey.create("directory to store LLVM IR from phases")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -250,6 +250,7 @@ fun CompilerConfiguration.setupFromArguments(arguments: K2NativeCompilerArgument
|
||||
arguments.testDumpOutputPath?.let { put(TEST_DUMP_OUTPUT_PATH, it) }
|
||||
put(PARTIAL_LINKAGE, arguments.partialLinkage)
|
||||
put(OMIT_FRAMEWORK_BINARY, arguments.omitFrameworkBinary)
|
||||
putIfNotNull(SAVE_LLVM_IR_DIRECTORY, arguments.saveLlvmIrDirectory)
|
||||
}
|
||||
|
||||
internal fun CompilerConfiguration.setupCommonOptionsForCaches(konanConfig: KonanConfig) = with(KonanConfigKeys) {
|
||||
|
||||
+17
-23
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.konan.driver.phases
|
||||
import llvm.LLVMDumpModule
|
||||
import llvm.LLVMModuleRef
|
||||
import llvm.LLVMWriteBitcodeToFile
|
||||
import org.jetbrains.kotlin.backend.common.phaser.Action
|
||||
import org.jetbrains.kotlin.backend.common.phaser.ActionState
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
|
||||
@@ -17,6 +16,7 @@ import org.jetbrains.kotlin.backend.konan.createLTOFinalPipelineConfig
|
||||
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
|
||||
import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
|
||||
import org.jetbrains.kotlin.backend.konan.driver.utilities.LlvmIrHolder
|
||||
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultLlvmModuleActions
|
||||
import org.jetbrains.kotlin.backend.konan.insertAliasToEntryPoint
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
|
||||
@@ -24,12 +24,6 @@ import org.jetbrains.kotlin.backend.konan.optimizations.RemoveRedundantSafepoint
|
||||
import org.jetbrains.kotlin.backend.konan.optimizations.removeMultipleThreadDataLoads
|
||||
import java.io.File
|
||||
|
||||
private val nativeLLVMDumper =
|
||||
fun(actionState: ActionState, _: Unit, context: NativeGenerationState) {
|
||||
llvmIrDumpCallback(actionState, context.llvm.module, context.config, context.tempFiles)
|
||||
}
|
||||
|
||||
private val llvmPhaseActions: Set<Action<Unit, NativeGenerationState>> = setOf(nativeLLVMDumper)
|
||||
|
||||
internal data class WriteBitcodeFileInput(
|
||||
override val llvmModule: LLVMModuleRef,
|
||||
@@ -48,26 +42,26 @@ internal val WriteBitcodeFilePhase = createSimpleNamedCompilerPhase<PhaseContext
|
||||
LLVMWriteBitcodeToFile(llvmModule, outputFile.canonicalPath)
|
||||
}
|
||||
|
||||
internal val CheckExternalCallsPhase = createSimpleNamedCompilerPhase(
|
||||
internal val CheckExternalCallsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "CheckExternalCalls",
|
||||
description = "Check external calls",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
) { context, _ ->
|
||||
checkLlvmModuleExternalCalls(context)
|
||||
}
|
||||
|
||||
internal val RewriteExternalCallsCheckerGlobals = createSimpleNamedCompilerPhase(
|
||||
internal val RewriteExternalCallsCheckerGlobals = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "RewriteExternalCallsCheckerGlobals",
|
||||
description = "Rewrite globals for external calls checker after optimizer run",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
) { context, _ ->
|
||||
addFunctionsListSymbolForChecker(context)
|
||||
}
|
||||
|
||||
internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase(
|
||||
internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "BitcodeOptimization",
|
||||
description = "Optimize bitcode",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
) { context, _ ->
|
||||
val config = createLTOFinalPipelineConfig(context, context.llvm.targetTriple, closedWorld = context.llvmModuleSpecification.isFinal)
|
||||
LlvmOptimizationPipeline(config, context.llvm.module, context).use {
|
||||
@@ -75,17 +69,17 @@ internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase(
|
||||
}
|
||||
}
|
||||
|
||||
internal val CoveragePhase = createSimpleNamedCompilerPhase(
|
||||
internal val CoveragePhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "Coverage",
|
||||
description = "Produce coverage information",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
op = { context, _ -> runCoveragePass(context) }
|
||||
)
|
||||
|
||||
internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase(
|
||||
internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "RemoveRedundantSafepoints",
|
||||
description = "Remove function prologue safepoints inlined to another function",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
op = { context, _ ->
|
||||
RemoveRedundantSafepointsPass().runOnModule(
|
||||
module = context.llvm.module,
|
||||
@@ -94,24 +88,24 @@ internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase(
|
||||
}
|
||||
)
|
||||
|
||||
internal val OptimizeTLSDataLoadsPhase = createSimpleNamedCompilerPhase(
|
||||
internal val OptimizeTLSDataLoadsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "OptimizeTLSDataLoads",
|
||||
description = "Optimize multiple loads of thread data",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
op = { context, _ -> removeMultipleThreadDataLoads(context) }
|
||||
)
|
||||
|
||||
internal val CStubsPhase = createSimpleNamedCompilerPhase(
|
||||
internal val CStubsPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "CStubs",
|
||||
description = "C stubs compilation",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
op = { context, _ -> produceCStubs(context) }
|
||||
)
|
||||
|
||||
internal val LinkBitcodeDependenciesPhase = createSimpleNamedCompilerPhase(
|
||||
internal val LinkBitcodeDependenciesPhase = createSimpleNamedCompilerPhase<NativeGenerationState, Unit>(
|
||||
name = "LinkBitcodeDependencies",
|
||||
description = "Link bitcode dependencies",
|
||||
postactions = llvmPhaseActions,
|
||||
postactions = getDefaultLlvmModuleActions(),
|
||||
op = { context, _ -> linkBitcodeDependencies(context) }
|
||||
)
|
||||
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ import llvm.DIFinalize
|
||||
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
|
||||
import org.jetbrains.kotlin.backend.konan.driver.utilities.KotlinBackendIrHolder
|
||||
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultIrActions
|
||||
import org.jetbrains.kotlin.backend.konan.driver.utilities.getDefaultLlvmModuleActions
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.CodeGeneratorVisitor
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.RTTIGeneratorVisitor
|
||||
@@ -60,8 +61,8 @@ internal data class CodegenInput(
|
||||
internal val CodegenPhase = createSimpleNamedCompilerPhase<NativeGenerationState, CodegenInput>(
|
||||
name = "Codegen",
|
||||
description = "Code generation",
|
||||
preactions = getDefaultIrActions(),
|
||||
postactions = getDefaultIrActions(),
|
||||
preactions = getDefaultIrActions<CodegenInput, NativeGenerationState>() + getDefaultLlvmModuleActions(),
|
||||
postactions = getDefaultIrActions<CodegenInput, NativeGenerationState>() + getDefaultLlvmModuleActions(),
|
||||
op = { generationState, input ->
|
||||
val context = generationState.context
|
||||
generationState.objCExport = ObjCExport(
|
||||
|
||||
+72
-1
@@ -5,8 +5,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.driver.utilities
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.LLVMGetModuleIdentifier
|
||||
import llvm.LLVMModuleRef
|
||||
import llvm.LLVMPrintModuleToFile
|
||||
import llvm.size_tVar
|
||||
import org.jetbrains.kotlin.backend.common.phaser.Action
|
||||
import org.jetbrains.kotlin.backend.common.phaser.ActionState
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Implementation of this interface in phase context, input or output
|
||||
@@ -14,4 +24,65 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
|
||||
*/
|
||||
interface LlvmIrHolder {
|
||||
val llvmModule: LLVMModuleRef
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create action that searches context and data for LLVM IR and dumps it.
|
||||
*/
|
||||
private fun <Data, Context : PhaseContext> createLlvmDumperAction(): Action<Data, Context> =
|
||||
fun(state: ActionState, data: Data, context: Context) {
|
||||
if (state.phase.name in context.config.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) {
|
||||
val llvmModule = findLlvmModule(data, context)
|
||||
if (llvmModule == null) {
|
||||
context.messageCollector.report(
|
||||
CompilerMessageSeverity.WARNING,
|
||||
"Cannot dump LLVM IR ${state.beforeOrAfter.name.lowercase()} ${state.phase.name}")
|
||||
return
|
||||
}
|
||||
val moduleName: String = memScoped {
|
||||
val sizeVar = alloc<size_tVar>()
|
||||
LLVMGetModuleIdentifier(llvmModule, sizeVar.ptr)!!.toKStringFromUtf8()
|
||||
}
|
||||
val output = File(context.config.saveLlvmIrDirectory, "$moduleName.${state.phase.name}.ll")
|
||||
if (LLVMPrintModuleToFile(llvmModule, output.absolutePath, null) != 0) {
|
||||
error("Can't dump LLVM IR to ${output.absolutePath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private fun <Data, Context : PhaseContext> createLlvmVerifierAction(): Action<Data, Context> =
|
||||
fun(actionState: ActionState, data: Data, context: Context) {
|
||||
if (!context.config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)) {
|
||||
return
|
||||
}
|
||||
val llvmModule = findLlvmModule(data, context)
|
||||
if (llvmModule == null) {
|
||||
context.messageCollector.report(
|
||||
CompilerMessageSeverity.WARNING,
|
||||
"Cannot verify LLVM IR ${actionState.beforeOrAfter.name.lowercase()} ${actionState.phase.name}")
|
||||
return
|
||||
}
|
||||
// TODO: Phase name in message
|
||||
verifyModule(llvmModule)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun <Data, Context : PhaseContext> findLlvmModule(data: Data, context: Context): LLVMModuleRef? = when {
|
||||
// TODO: Not safe at all
|
||||
data is CPointer<*> -> data as LLVMModuleRef
|
||||
data is LlvmIrHolder -> data.llvmModule
|
||||
context is LlvmIrHolder -> context.llvmModule
|
||||
else -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* Default set of dump and validate actions for LLVM phases.
|
||||
*/
|
||||
internal fun <Data, Context : PhaseContext> getDefaultLlvmModuleActions(): Set<Action<Data, Context>> =
|
||||
setOf(createLlvmDumperAction(), createLlvmVerifierAction())
|
||||
Reference in New Issue
Block a user