Support sanitizers in runtime tests (#4622)
This commit is contained in:
committed by
Nikolay Krasko
parent
3ee8d98726
commit
6cc60ad6a2
+26
-6
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ExecClang
|
|||||||
import org.jetbrains.kotlin.konan.target.Family
|
import org.jetbrains.kotlin.konan.target.Family
|
||||||
import org.jetbrains.kotlin.konan.target.HostManager
|
import org.jetbrains.kotlin.konan.target.HostManager
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.konan.target.SanitizerKind
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ open class CompileToBitcode @Inject constructor(
|
|||||||
val srcRoot: File,
|
val srcRoot: File,
|
||||||
val folderName: String,
|
val folderName: String,
|
||||||
val target: String,
|
val target: String,
|
||||||
val outputGroup: String
|
val outputGroup: String,
|
||||||
) : DefaultTask() {
|
) : DefaultTask() {
|
||||||
|
|
||||||
enum class Language {
|
enum class Language {
|
||||||
@@ -46,9 +47,22 @@ open class CompileToBitcode @Inject constructor(
|
|||||||
@Input
|
@Input
|
||||||
var language = Language.CPP
|
var language = Language.CPP
|
||||||
|
|
||||||
private val targetDir by lazy { project.buildDir.resolve("bitcode/$outputGroup/$target") }
|
@Input @Optional
|
||||||
|
var sanitizer: SanitizerKind? = null
|
||||||
|
|
||||||
val objDir by lazy { File(targetDir, folderName) }
|
private val targetDir: File
|
||||||
|
get() {
|
||||||
|
val sanitizerSuffix = when (sanitizer) {
|
||||||
|
null -> ""
|
||||||
|
SanitizerKind.ADDRESS -> "-asan"
|
||||||
|
SanitizerKind.THREAD -> "-tsan"
|
||||||
|
}
|
||||||
|
return project.buildDir.resolve("bitcode/$outputGroup/$target$sanitizerSuffix")
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
val objDir
|
||||||
|
get() = File(targetDir, folderName)
|
||||||
|
|
||||||
private val KonanTarget.isMINGW
|
private val KonanTarget.isMINGW
|
||||||
get() = this.family == Family.MINGW
|
get() = this.family == Family.MINGW
|
||||||
@@ -63,6 +77,11 @@ open class CompileToBitcode @Inject constructor(
|
|||||||
val compilerFlags: List<String>
|
val compilerFlags: List<String>
|
||||||
get() {
|
get() {
|
||||||
val commonFlags = listOf("-c", "-emit-llvm") + headersDirs.map { "-I$it" }
|
val commonFlags = listOf("-c", "-emit-llvm") + headersDirs.map { "-I$it" }
|
||||||
|
val sanitizerFlags = when (sanitizer) {
|
||||||
|
null -> listOf()
|
||||||
|
SanitizerKind.ADDRESS -> listOf("-fsanitize=address")
|
||||||
|
SanitizerKind.THREAD -> listOf("-fsanitize=thread")
|
||||||
|
}
|
||||||
val languageFlags = when (language) {
|
val languageFlags = when (language) {
|
||||||
Language.C ->
|
Language.C ->
|
||||||
// Used flags provided by original build of allocator C code.
|
// Used flags provided by original build of allocator C code.
|
||||||
@@ -73,7 +92,7 @@ open class CompileToBitcode @Inject constructor(
|
|||||||
"-Wno-unused-parameter", // False positives with polymorphic functions.
|
"-Wno-unused-parameter", // False positives with polymorphic functions.
|
||||||
"-fPIC".takeIf { !HostManager().targetByName(target).isMINGW })
|
"-fPIC".takeIf { !HostManager().targetByName(target).isMINGW })
|
||||||
}
|
}
|
||||||
return commonFlags + languageFlags + compilerArgs
|
return commonFlags + sanitizerFlags + languageFlags + compilerArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:SkipWhenEmpty
|
@get:SkipWhenEmpty
|
||||||
@@ -127,8 +146,9 @@ open class CompileToBitcode @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OutputFile
|
@get:OutputFile
|
||||||
val outFile = File(targetDir, "${folderName}.bc")
|
val outFile: File
|
||||||
|
get() = File(targetDir, "${folderName}.bc")
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun compile() {
|
fun compile() {
|
||||||
|
|||||||
+31
-8
@@ -9,6 +9,9 @@ import org.gradle.api.Plugin
|
|||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.plugins.BasePlugin
|
import org.gradle.api.plugins.BasePlugin
|
||||||
import org.jetbrains.kotlin.createCompilationDatabasesFromCompileToBitcodeTasks
|
import org.jetbrains.kotlin.createCompilationDatabasesFromCompileToBitcodeTasks
|
||||||
|
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||||
|
import org.jetbrains.kotlin.konan.target.SanitizerKind
|
||||||
|
import org.jetbrains.kotlin.konan.target.supportedSanitizers
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -45,20 +48,40 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) {
|
|||||||
configurationBlock: CompileToBitcode.() -> Unit = {}
|
configurationBlock: CompileToBitcode.() -> Unit = {}
|
||||||
) {
|
) {
|
||||||
targetList.get().forEach { targetName ->
|
targetList.get().forEach { targetName ->
|
||||||
project.tasks.register(
|
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
|
||||||
"${targetName}${name.snakeCaseToCamelCase().capitalize()}",
|
val target = platformManager.targetByName(targetName)
|
||||||
CompileToBitcode::class.java,
|
val sanitizers: List<SanitizerKind?> = target.supportedSanitizers() + listOf(null)
|
||||||
srcDir, name, targetName, outputGroup
|
sanitizers.forEach { sanitizer ->
|
||||||
).configure {
|
project.tasks.register(
|
||||||
it.group = BasePlugin.BUILD_GROUP
|
"${targetName}${name.snakeCaseToCamelCase().capitalize()}${suffixForSanitizer(sanitizer)}",
|
||||||
it.description = "Compiles '$name' to bitcode for $targetName"
|
CompileToBitcode::class.java,
|
||||||
it.configurationBlock()
|
srcDir, name, targetName, outputGroup
|
||||||
|
).configure {
|
||||||
|
it.sanitizer = sanitizer
|
||||||
|
it.group = BasePlugin.BUILD_GROUP
|
||||||
|
val sanitizerDescription = when (sanitizer) {
|
||||||
|
null -> ""
|
||||||
|
SanitizerKind.ADDRESS -> " with ASAN"
|
||||||
|
SanitizerKind.THREAD -> " with TSAN"
|
||||||
|
}
|
||||||
|
it.description = "Compiles '$name' to bitcode for $targetName$sanitizerDescription"
|
||||||
|
it.configurationBlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun String.snakeCaseToCamelCase() =
|
private fun String.snakeCaseToCamelCase() =
|
||||||
split('_').joinToString(separator = "") { it.capitalize() }
|
split('_').joinToString(separator = "") { it.capitalize() }
|
||||||
|
|
||||||
|
fun suffixForSanitizer(sanitizer: SanitizerKind?) =
|
||||||
|
when (sanitizer) {
|
||||||
|
null -> ""
|
||||||
|
SanitizerKind.ADDRESS -> "_ASAN"
|
||||||
|
SanitizerKind.THREAD -> "_TSAN"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-16
@@ -13,6 +13,7 @@ import org.gradle.api.file.ConfigurableFileCollection
|
|||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.jetbrains.kotlin.ExecClang
|
import org.jetbrains.kotlin.ExecClang
|
||||||
import org.jetbrains.kotlin.bitcode.CompileToBitcode
|
import org.jetbrains.kotlin.bitcode.CompileToBitcode
|
||||||
|
import org.jetbrains.kotlin.bitcode.CompileToBitcodeExtension
|
||||||
import org.jetbrains.kotlin.konan.target.*
|
import org.jetbrains.kotlin.konan.target.*
|
||||||
|
|
||||||
open class CompileNativeTest @Inject constructor(
|
open class CompileNativeTest @Inject constructor(
|
||||||
@@ -25,18 +26,29 @@ open class CompileNativeTest @Inject constructor(
|
|||||||
@Input
|
@Input
|
||||||
val clangArgs = mutableListOf<String>()
|
val clangArgs = mutableListOf<String>()
|
||||||
|
|
||||||
|
@Input @Optional
|
||||||
|
var sanitizer: SanitizerKind? = null
|
||||||
|
|
||||||
|
@Input
|
||||||
|
private val sanitizerFlags = when (sanitizer) {
|
||||||
|
null -> listOf()
|
||||||
|
SanitizerKind.ADDRESS -> listOf("-fsanitize=address")
|
||||||
|
SanitizerKind.THREAD -> listOf("-fsanitize=thread")
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun compile() {
|
fun compile() {
|
||||||
val plugin = project.convention.getPlugin(ExecClang::class.java)
|
val plugin = project.convention.getPlugin(ExecClang::class.java)
|
||||||
|
val args = clangArgs + sanitizerFlags + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
||||||
if (target.family.isAppleFamily) {
|
if (target.family.isAppleFamily) {
|
||||||
plugin.execToolchainClang(target) {
|
plugin.execToolchainClang(target) {
|
||||||
it.executable = "clang++"
|
it.executable = "clang++"
|
||||||
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
it.args = args
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
plugin.execBareClang {
|
plugin.execBareClang {
|
||||||
it.executable = "clang++"
|
it.executable = "clang++"
|
||||||
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
|
it.args = args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +104,7 @@ open class LinkNativeTest @Inject constructor(
|
|||||||
@Internal val target: String,
|
@Internal val target: String,
|
||||||
@Internal val linkerArgs: List<String>,
|
@Internal val linkerArgs: List<String>,
|
||||||
private val platformManager: PlatformManager,
|
private val platformManager: PlatformManager,
|
||||||
private val mimallocEnabled: Boolean
|
private val mimallocEnabled: Boolean,
|
||||||
) : DefaultTask () {
|
) : DefaultTask () {
|
||||||
companion object {
|
companion object {
|
||||||
fun create(
|
fun create(
|
||||||
@@ -103,7 +115,7 @@ open class LinkNativeTest @Inject constructor(
|
|||||||
target: String,
|
target: String,
|
||||||
outputFile: File,
|
outputFile: File,
|
||||||
linkerArgs: List<String>,
|
linkerArgs: List<String>,
|
||||||
mimallocEnabled: Boolean
|
mimallocEnabled: Boolean,
|
||||||
): LinkNativeTest = project.tasks.create(
|
): LinkNativeTest = project.tasks.create(
|
||||||
taskName,
|
taskName,
|
||||||
LinkNativeTest::class.java,
|
LinkNativeTest::class.java,
|
||||||
@@ -133,6 +145,9 @@ open class LinkNativeTest @Inject constructor(
|
|||||||
linkerArgs, mimallocEnabled)
|
linkerArgs, mimallocEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input @Optional
|
||||||
|
var sanitizer: SanitizerKind? = null
|
||||||
|
|
||||||
@get:Input
|
@get:Input
|
||||||
val commands: List<List<String>>
|
val commands: List<List<String>>
|
||||||
get() {
|
get() {
|
||||||
@@ -149,7 +164,8 @@ open class LinkNativeTest @Inject constructor(
|
|||||||
kind = LinkerOutputKind.EXECUTABLE,
|
kind = LinkerOutputKind.EXECUTABLE,
|
||||||
outputDsymBundle = "",
|
outputDsymBundle = "",
|
||||||
needsProfileLibrary = false,
|
needsProfileLibrary = false,
|
||||||
mimallocEnabled = mimallocEnabled
|
mimallocEnabled = mimallocEnabled,
|
||||||
|
sanitizer = sanitizer,
|
||||||
).map { it.argsWithExecutable }
|
).map { it.argsWithExecutable }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +179,11 @@ open class LinkNativeTest @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTestTask(
|
private fun createTestTask(
|
||||||
project: Project,
|
project: Project,
|
||||||
testName: String,
|
testName: String,
|
||||||
testTaskName: String,
|
|
||||||
testedTaskNames: List<String>,
|
testedTaskNames: List<String>,
|
||||||
|
sanitizer: SanitizerKind?,
|
||||||
configureCompileToBitcode: CompileToBitcode.() -> Unit = {},
|
configureCompileToBitcode: CompileToBitcode.() -> Unit = {},
|
||||||
): Task {
|
): Task {
|
||||||
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
|
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
|
||||||
@@ -188,6 +204,7 @@ fun createTestTask(
|
|||||||
"${it.folderName}Tests",
|
"${it.folderName}Tests",
|
||||||
target, "test"
|
target, "test"
|
||||||
).apply {
|
).apply {
|
||||||
|
this.sanitizer = sanitizer
|
||||||
excludeFiles = emptyList()
|
excludeFiles = emptyList()
|
||||||
includeFiles = listOf("**/*Test.cpp", "**/*Test.mm")
|
includeFiles = listOf("**/*Test.cpp", "**/*Test.mm")
|
||||||
dependsOn(it)
|
dependsOn(it)
|
||||||
@@ -201,18 +218,19 @@ fun createTestTask(
|
|||||||
else
|
else
|
||||||
task
|
task
|
||||||
}
|
}
|
||||||
|
// TODO: Consider using sanitized versions.
|
||||||
val testFrameworkTasks = listOf(
|
val testFrameworkTasks = listOf(
|
||||||
project.tasks.getByName("${target}Googletest") as CompileToBitcode,
|
project.tasks.getByName("${target}Googletest") as CompileToBitcode,
|
||||||
project.tasks.getByName("${target}Googlemock") as CompileToBitcode
|
project.tasks.getByName("${target}Googlemock") as CompileToBitcode
|
||||||
)
|
)
|
||||||
|
|
||||||
val testSupportTask = project.tasks.getByName("${target}TestSupport") as CompileToBitcode
|
val testSupportTask = project.tasks.getByName("${target}TestSupport${CompileToBitcodeExtension.suffixForSanitizer(sanitizer)}") as CompileToBitcode
|
||||||
|
|
||||||
// TODO: It may make sense to merge llvm-link, compile and link to a single task.
|
// TODO: It may make sense to merge llvm-link, compile and link to a single task.
|
||||||
val llvmLinkTask = project.tasks.create(
|
val llvmLinkTask = project.tasks.create(
|
||||||
"${testTaskName}LlvmLink",
|
"${testName}LlvmLink",
|
||||||
LlvmLinkNativeTest::class.java,
|
LlvmLinkNativeTest::class.java,
|
||||||
testTaskName, target, testSupportTask.outFile
|
testName, target, testSupportTask.outFile
|
||||||
).apply {
|
).apply {
|
||||||
val tasksToLink = (compileToBitcodeTasks + testedTasks + testFrameworkTasks)
|
val tasksToLink = (compileToBitcodeTasks + testedTasks + testFrameworkTasks)
|
||||||
inputFiles = project.files(tasksToLink.map { it.outFile })
|
inputFiles = project.files(tasksToLink.map { it.outFile })
|
||||||
@@ -222,11 +240,12 @@ fun createTestTask(
|
|||||||
|
|
||||||
val clangFlags = platformManager.platform(konanTarget).configurables as ClangFlags
|
val clangFlags = platformManager.platform(konanTarget).configurables as ClangFlags
|
||||||
val compileTask = project.tasks.create(
|
val compileTask = project.tasks.create(
|
||||||
"${testTaskName}Compile",
|
"${testName}Compile",
|
||||||
CompileNativeTest::class.java,
|
CompileNativeTest::class.java,
|
||||||
llvmLinkTask.outputFile,
|
llvmLinkTask.outputFile,
|
||||||
konanTarget,
|
konanTarget,
|
||||||
).apply {
|
).apply {
|
||||||
|
this.sanitizer = sanitizer
|
||||||
dependsOn(llvmLinkTask)
|
dependsOn(llvmLinkTask)
|
||||||
clangArgs.addAll(clangFlags.clangFlags)
|
clangArgs.addAll(clangFlags.clangFlags)
|
||||||
clangArgs.addAll(clangFlags.clangNooptFlags)
|
clangArgs.addAll(clangFlags.clangNooptFlags)
|
||||||
@@ -236,19 +255,20 @@ fun createTestTask(
|
|||||||
val linkTask = LinkNativeTest.create(
|
val linkTask = LinkNativeTest.create(
|
||||||
project,
|
project,
|
||||||
platformManager,
|
platformManager,
|
||||||
"${testTaskName}Link",
|
"${testName}Link${CompileToBitcodeExtension.suffixForSanitizer(sanitizer)}",
|
||||||
listOf(compileTask.outputFile),
|
listOf(compileTask.outputFile),
|
||||||
target,
|
target,
|
||||||
testTaskName,
|
testName,
|
||||||
mimallocEnabled
|
mimallocEnabled,
|
||||||
).apply {
|
).apply {
|
||||||
|
this.sanitizer = sanitizer
|
||||||
dependsOn(compileTask)
|
dependsOn(compileTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
return project.tasks.create(testTaskName, Exec::class.java).apply {
|
return project.tasks.create(testName, Exec::class.java).apply {
|
||||||
dependsOn(linkTask)
|
dependsOn(linkTask)
|
||||||
|
|
||||||
workingDir = project.buildDir.resolve("testReports/$testTaskName")
|
workingDir = project.buildDir.resolve("testReports/$testName")
|
||||||
val xmlReport = workingDir.resolve("report.xml")
|
val xmlReport = workingDir.resolve("report.xml")
|
||||||
executable(linkTask.outputFile)
|
executable(linkTask.outputFile)
|
||||||
args("--gtest_output=xml:${xmlReport.absoluteFile}")
|
args("--gtest_output=xml:${xmlReport.absoluteFile}")
|
||||||
@@ -267,3 +287,24 @@ fun createTestTask(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: These tests should be created by `CompileToBitcodeExtension`
|
||||||
|
fun createTestTasks(
|
||||||
|
project: Project,
|
||||||
|
targetName: String,
|
||||||
|
testTaskName: String,
|
||||||
|
testedTaskNames: List<String>,
|
||||||
|
configureCompileToBitcode: CompileToBitcode.() -> Unit = {},
|
||||||
|
): List<Task> {
|
||||||
|
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
|
||||||
|
val target = platformManager.targetByName(targetName)
|
||||||
|
val sanitizers: List<SanitizerKind?> = target.supportedSanitizers() + listOf(null)
|
||||||
|
return sanitizers.map { sanitizer ->
|
||||||
|
val suffix = CompileToBitcodeExtension.suffixForSanitizer(sanitizer)
|
||||||
|
val name = testTaskName + suffix
|
||||||
|
val testedNames = testedTaskNames.map {
|
||||||
|
it + suffix
|
||||||
|
}
|
||||||
|
createTestTask(project, name, testedNames, sanitizer, configureCompileToBitcode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
import org.jetbrains.kotlin.*
|
import org.jetbrains.kotlin.*
|
||||||
import org.jetbrains.kotlin.testing.native.*
|
import org.jetbrains.kotlin.testing.native.*
|
||||||
import org.jetbrains.kotlin.bitcode.CompileToBitcode
|
import org.jetbrains.kotlin.bitcode.CompileToBitcode
|
||||||
|
import org.jetbrains.kotlin.bitcode.CompileToBitcodeExtension
|
||||||
|
import org.jetbrains.kotlin.konan.target.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("compile-to-bitcode")
|
id("compile-to-bitcode")
|
||||||
@@ -42,6 +44,7 @@ bitcode {
|
|||||||
"${target}ExperimentalMemoryManager"
|
"${target}ExperimentalMemoryManager"
|
||||||
)
|
)
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
|
// TODO: Should depend on the sanitizer.
|
||||||
linkerArgs.add(project.file("../common/build/bitcode/main/$target/hash.bc").path)
|
linkerArgs.add(project.file("../common/build/bitcode/main/$target/hash.bc").path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,9 +110,11 @@ bitcode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList.forEach { targetName ->
|
targetList.forEach { targetName ->
|
||||||
createTestTask(
|
val allTests = mutableListOf<Task>()
|
||||||
|
|
||||||
|
allTests.addAll(createTestTasks(
|
||||||
project,
|
project,
|
||||||
"StdAlloc",
|
targetName,
|
||||||
"${targetName}StdAllocRuntimeTests",
|
"${targetName}StdAllocRuntimeTests",
|
||||||
listOf(
|
listOf(
|
||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
@@ -120,11 +125,11 @@ targetList.forEach { targetName ->
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
})
|
||||||
|
|
||||||
createTestTask(
|
allTests.addAll(createTestTasks(
|
||||||
project,
|
project,
|
||||||
"Mimalloc",
|
targetName,
|
||||||
"${targetName}MimallocRuntimeTests",
|
"${targetName}MimallocRuntimeTests",
|
||||||
listOf(
|
listOf(
|
||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
@@ -136,11 +141,11 @@ targetList.forEach { targetName ->
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
})
|
||||||
|
|
||||||
createTestTask(
|
allTests.addAll(createTestTasks(
|
||||||
project,
|
project,
|
||||||
"ExperimentalMMMimalloc",
|
targetName,
|
||||||
"${targetName}ExperimentalMMMimallocRuntimeTests",
|
"${targetName}ExperimentalMMMimallocRuntimeTests",
|
||||||
listOf(
|
listOf(
|
||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
@@ -151,11 +156,11 @@ targetList.forEach { targetName ->
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
})
|
||||||
|
|
||||||
createTestTask(
|
allTests.addAll(createTestTasks(
|
||||||
project,
|
project,
|
||||||
"ExperimentalMMStdAlloc",
|
targetName,
|
||||||
"${targetName}ExperimentalMMStdAllocRuntimeTests",
|
"${targetName}ExperimentalMMStdAllocRuntimeTests",
|
||||||
listOf(
|
listOf(
|
||||||
"${targetName}Runtime",
|
"${targetName}Runtime",
|
||||||
@@ -165,13 +170,11 @@ targetList.forEach { targetName ->
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
includeRuntime()
|
includeRuntime()
|
||||||
}
|
})
|
||||||
|
|
||||||
|
// TODO: This "all tests" tasks should be provided by `CompileToBitcodeExtension`
|
||||||
tasks.register("${targetName}RuntimeTests") {
|
tasks.register("${targetName}RuntimeTests") {
|
||||||
dependsOn("${targetName}StdAllocRuntimeTests")
|
dependsOn(allTests)
|
||||||
dependsOn("${targetName}MimallocRuntimeTests")
|
|
||||||
dependsOn("${targetName}ExperimentalMMStdAllocRuntimeTests")
|
|
||||||
dependsOn("${targetName}ExperimentalMMMimallocRuntimeTests")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,9 @@ TEST_F(KonanAllocatorAwareTest, PlacementAllocated) {
|
|||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
||||||
constexpr size_t kCount = 5;
|
constexpr size_t kCount = 5;
|
||||||
std::array<uint8_t, sizeof(A) * kCount> buffer;
|
// TODO: Consider removing support for placement new[] altogether, since there's no
|
||||||
|
// portable way to know needed storage size ahead of time.
|
||||||
|
alignas(A) std::array<uint8_t, sizeof(A) * kCount + sizeof(size_t)> buffer;
|
||||||
A* as = new (buffer.data()) A[kCount];
|
A* as = new (buffer.data()) A[kCount];
|
||||||
|
|
||||||
std::vector<int> actual;
|
std::vector<int> actual;
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ namespace kotlin {
|
|||||||
#if KONAN_WINDOWS
|
#if KONAN_WINDOWS
|
||||||
// TODO: Figure out why creating many threads on windows is so slow.
|
// TODO: Figure out why creating many threads on windows is so slow.
|
||||||
constexpr int kDefaultThreadCount = 10;
|
constexpr int kDefaultThreadCount = 10;
|
||||||
|
#elif __has_feature(thread_sanitizer)
|
||||||
|
// TSAN has a huge overhead.
|
||||||
|
constexpr int kDefaultThreadCount = 10;
|
||||||
#else
|
#else
|
||||||
constexpr int kDefaultThreadCount = 100;
|
constexpr int kDefaultThreadCount = 100;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,9 +14,27 @@
|
|||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ALWAYS_INLINE T UnsafeRead(T* location) noexcept {
|
||||||
|
#if __has_feature(thread_sanitizer)
|
||||||
|
// Make TSAN think that this load is fine.
|
||||||
|
return __atomic_load_n(location, __ATOMIC_ACQUIRE);
|
||||||
|
#else
|
||||||
|
return *location;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
mm::ExtraObjectData& mm::ExtraObjectData::Install(ObjHeader* object) noexcept {
|
mm::ExtraObjectData& mm::ExtraObjectData::Install(ObjHeader* object) noexcept {
|
||||||
TypeInfo* typeInfo = object->typeInfoOrMeta_;
|
// TODO: Consider extracting initialization scheme with speculative load.
|
||||||
|
// `object->typeInfoOrMeta_` is assigned at most once. If we read some old value (i.e. not a meta object),
|
||||||
|
// we will fail at CAS below. If we read the new value, we will immediately return it.
|
||||||
|
TypeInfo* typeInfo = UnsafeRead(&object->typeInfoOrMeta_);
|
||||||
|
|
||||||
if (auto* metaObject = ObjHeader::AsMetaObject(typeInfo)) {
|
if (auto* metaObject = ObjHeader::AsMetaObject(typeInfo)) {
|
||||||
return mm::ExtraObjectData::FromMetaObjHeader(metaObject);
|
return mm::ExtraObjectData::FromMetaObjHeader(metaObject);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -30,3 +30,14 @@ fun KonanTarget.supportsThreads(): Boolean =
|
|||||||
is KonanTarget.ZEPHYR -> false
|
is KonanTarget.ZEPHYR -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KonanTarget.supportedSanitizers(): List<SanitizerKind> =
|
||||||
|
when(this) {
|
||||||
|
is KonanTarget.LINUX_X64 -> listOf(SanitizerKind.ADDRESS)
|
||||||
|
is KonanTarget.MACOS_X64 -> listOf(SanitizerKind.THREAD)
|
||||||
|
// TODO: Enable ASAN on macOS. Currently there's an incompatibility between clang frontend version and clang_rt.asan version.
|
||||||
|
// TODO: Enable TSAN on linux. Currently there's a link error between clang_rt.tsan and libstdc++.
|
||||||
|
// TODO: Consider supporting mingw.
|
||||||
|
// TODO: Support macOS arm64
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ abstract class LinkerFlags(val configurables: Configurables) {
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command>
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
|
sanitizer: SanitizerKind? = null): List<Command>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list of commands that link object files into a single one.
|
* Returns list of commands that link object files into a single one.
|
||||||
@@ -93,7 +94,7 @@ abstract class LinkerFlags(val configurables: Configurables) {
|
|||||||
return libraries
|
return libraries
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun provideCompilerRtLibrary(libraryName: String): String? {
|
protected open fun provideCompilerRtLibrary(libraryName: String, isDynamic: Boolean = false): String? {
|
||||||
System.err.println("Can't provide $libraryName.")
|
System.err.println("Can't provide $libraryName.")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,11 @@ class AndroidLinker(targetProperties: AndroidConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||||
|
|
||||||
@@ -170,7 +175,12 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
get() = this == KonanTarget.TVOS_X64 || this == KonanTarget.IOS_X64 ||
|
get() = this == KonanTarget.TVOS_X64 || this == KonanTarget.IOS_X64 ||
|
||||||
this == KonanTarget.WATCHOS_X86 || this == KonanTarget.WATCHOS_X64
|
this == KonanTarget.WATCHOS_X86 || this == KonanTarget.WATCHOS_X64
|
||||||
|
|
||||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
private val compilerRtDir: String? by lazy {
|
||||||
|
val dir = File("$absoluteTargetToolchain/usr/lib/clang/").listFiles.firstOrNull()?.absolutePath
|
||||||
|
if (dir != null) "$dir/lib/darwin/" else null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun provideCompilerRtLibrary(libraryName: String, isDynamic: Boolean): String? {
|
||||||
val prefix = when (target.family) {
|
val prefix = when (target.family) {
|
||||||
Family.IOS -> "ios"
|
Family.IOS -> "ios"
|
||||||
Family.WATCHOS -> "watchos"
|
Family.WATCHOS -> "watchos"
|
||||||
@@ -184,10 +194,11 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
val dir = File("$absoluteTargetToolchain/usr/lib/clang/").listFiles.firstOrNull()?.absolutePath
|
val dir = compilerRtDir
|
||||||
val mangledLibraryName = if (libraryName.isEmpty()) "" else "${libraryName}_"
|
val mangledLibraryName = if (libraryName.isEmpty()) "" else "${libraryName}_"
|
||||||
|
val extension = if (isDynamic) "_dynamic.dylib" else ".a"
|
||||||
|
|
||||||
return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledLibraryName$prefix$suffix.a" else null
|
return if (dir != null) "$dir/libclang_rt.$mangledLibraryName$prefix$suffix$extension" else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private val osVersionMinFlags: List<String> by lazy {
|
private val osVersionMinFlags: List<String> by lazy {
|
||||||
@@ -210,14 +221,19 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean, kind: LinkerOutputKind,
|
optimize: Boolean, debug: Boolean, kind: LinkerOutputKind,
|
||||||
outputDsymBundle: String,
|
outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
|
if (kind == LinkerOutputKind.STATIC_LIBRARY) {
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
return listOf(Command(libtool).apply {
|
return listOf(Command(libtool).apply {
|
||||||
+"-static"
|
+"-static"
|
||||||
+listOf("-o", executable)
|
+listOf("-o", executable)
|
||||||
+objectFiles
|
+objectFiles
|
||||||
+libraries
|
+libraries
|
||||||
})
|
})
|
||||||
|
}
|
||||||
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
|
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
|
||||||
|
|
||||||
val result = mutableListOf<Command>()
|
val result = mutableListOf<Command>()
|
||||||
@@ -237,7 +253,12 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
if (needsProfileLibrary) +profileLibrary!!
|
if (needsProfileLibrary) +profileLibrary!!
|
||||||
+libraries
|
+libraries
|
||||||
+linkerArgs
|
+linkerArgs
|
||||||
+rpath(dynamic)
|
+rpath(dynamic, sanitizer)
|
||||||
|
when (sanitizer) {
|
||||||
|
null -> {}
|
||||||
|
SanitizerKind.ADDRESS -> +provideCompilerRtLibrary("asan", isDynamic=true)!!
|
||||||
|
SanitizerKind.THREAD -> +provideCompilerRtLibrary("tsan", isDynamic=true)!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: revise debug information handling.
|
// TODO: revise debug information handling.
|
||||||
@@ -255,7 +276,7 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
provideCompilerRtLibrary("")
|
provideCompilerRtLibrary("")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rpath(dynamic: Boolean): List<String> = listOfNotNull(
|
private fun rpath(dynamic: Boolean, sanitizer: SanitizerKind?): List<String> = listOfNotNull(
|
||||||
when (target.family) {
|
when (target.family) {
|
||||||
Family.OSX -> "@executable_path/../Frameworks"
|
Family.OSX -> "@executable_path/../Frameworks"
|
||||||
Family.IOS,
|
Family.IOS,
|
||||||
@@ -263,7 +284,8 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
|||||||
Family.TVOS -> "@executable_path/Frameworks"
|
Family.TVOS -> "@executable_path/Frameworks"
|
||||||
else -> error(target)
|
else -> error(target)
|
||||||
},
|
},
|
||||||
"@loader_path/Frameworks".takeIf { dynamic }
|
"@loader_path/Frameworks".takeIf { dynamic },
|
||||||
|
compilerRtDir.takeIf { sanitizer != null },
|
||||||
).flatMap { listOf("-rpath", it) }
|
).flatMap { listOf("-rpath", it) }
|
||||||
|
|
||||||
fun dsymUtilCommand(executable: ExecutableFile, outputDsymBundle: String) =
|
fun dsymUtilCommand(executable: ExecutableFile, outputDsymBundle: String) =
|
||||||
@@ -319,7 +341,10 @@ class GccBasedLinker(targetProperties: GccConfigurables)
|
|||||||
|
|
||||||
private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
|
private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" }
|
||||||
|
|
||||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
override fun provideCompilerRtLibrary(libraryName: String, isDynamic: Boolean): String? {
|
||||||
|
require(!isDynamic) {
|
||||||
|
"Dynamic compiler rt librares are unsupported"
|
||||||
|
}
|
||||||
val targetSuffix = when (target) {
|
val targetSuffix = when (target) {
|
||||||
KonanTarget.LINUX_X64 -> "x86_64"
|
KonanTarget.LINUX_X64 -> "x86_64"
|
||||||
else -> error("$target is not supported.")
|
else -> error("$target is not supported.")
|
||||||
@@ -334,9 +359,14 @@ class GccBasedLinker(targetProperties: GccConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
|
if (kind == LinkerOutputKind.STATIC_LIBRARY) {
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||||
|
}
|
||||||
val isMips = target == KonanTarget.LINUX_MIPS32 || target == KonanTarget.LINUX_MIPSEL32
|
val isMips = target == KonanTarget.LINUX_MIPS32 || target == KonanTarget.LINUX_MIPSEL32
|
||||||
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
|
val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY
|
||||||
val crtPrefix = "$absoluteTargetSysRoot/$crtFilesLocation"
|
val crtPrefix = "$absoluteTargetSysRoot/$crtFilesLocation"
|
||||||
@@ -373,6 +403,19 @@ class GccBasedLinker(targetProperties: GccConfigurables)
|
|||||||
+linkerGccFlags
|
+linkerGccFlags
|
||||||
+if (dynamic) "$libGcc/crtendS.o" else "$libGcc/crtend.o"
|
+if (dynamic) "$libGcc/crtendS.o" else "$libGcc/crtend.o"
|
||||||
+"$crtPrefix/crtn.o"
|
+"$crtPrefix/crtn.o"
|
||||||
|
when (sanitizer) {
|
||||||
|
null -> {}
|
||||||
|
SanitizerKind.ADDRESS -> {
|
||||||
|
+"-lrt"
|
||||||
|
+provideCompilerRtLibrary("asan")!!
|
||||||
|
+provideCompilerRtLibrary("asan_cxx")!!
|
||||||
|
}
|
||||||
|
SanitizerKind.THREAD -> {
|
||||||
|
+"-lrt"
|
||||||
|
+provideCompilerRtLibrary("tsan")!!
|
||||||
|
+provideCompilerRtLibrary("tsan_cxx")!!
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +430,10 @@ class MingwLinker(targetProperties: MingwConfigurables)
|
|||||||
|
|
||||||
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
|
override fun filterStaticLibraries(binaries: List<String>) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib }
|
||||||
|
|
||||||
override fun provideCompilerRtLibrary(libraryName: String): String? {
|
override fun provideCompilerRtLibrary(libraryName: String, isDynamic: Boolean): String? {
|
||||||
|
require(!isDynamic) {
|
||||||
|
"Dynamic compiler rt librares are unsupported"
|
||||||
|
}
|
||||||
val targetSuffix = when (target) {
|
val targetSuffix = when (target) {
|
||||||
KonanTarget.MINGW_X64 -> "x86_64"
|
KonanTarget.MINGW_X64 -> "x86_64"
|
||||||
else -> error("$target is not supported.")
|
else -> error("$target is not supported.")
|
||||||
@@ -400,7 +446,11 @@ class MingwLinker(targetProperties: MingwConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
if (kind == LinkerOutputKind.STATIC_LIBRARY)
|
||||||
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
return staticGnuArCommands(ar, executable, objectFiles, libraries)
|
||||||
|
|
||||||
@@ -437,8 +487,12 @@ class WasmLinker(targetProperties: WasmConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind")
|
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind")
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
|
|
||||||
val linkage = Command("$llvmBin/wasm-ld").apply {
|
val linkage = Command("$llvmBin/wasm-ld").apply {
|
||||||
+objectFiles
|
+objectFiles
|
||||||
@@ -489,8 +543,12 @@ open class ZephyrLinker(targetProperties: ZephyrConfigurables)
|
|||||||
libraries: List<String>, linkerArgs: List<String>,
|
libraries: List<String>, linkerArgs: List<String>,
|
||||||
optimize: Boolean, debug: Boolean,
|
optimize: Boolean, debug: Boolean,
|
||||||
kind: LinkerOutputKind, outputDsymBundle: String,
|
kind: LinkerOutputKind, outputDsymBundle: String,
|
||||||
needsProfileLibrary: Boolean, mimallocEnabled: Boolean): List<Command> {
|
needsProfileLibrary: Boolean, mimallocEnabled: Boolean,
|
||||||
|
sanitizer: SanitizerKind?): List<Command> {
|
||||||
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind")
|
if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind")
|
||||||
|
require(sanitizer == null) {
|
||||||
|
"Sanitizers are unsupported"
|
||||||
|
}
|
||||||
return listOf(Command(linker).apply {
|
return listOf(Command(linker).apply {
|
||||||
+listOf("-r", "--gc-sections", "--entry", "main")
|
+listOf("-r", "--gc-sections", "--entry", "main")
|
||||||
+listOf("-o", executable)
|
+listOf("-o", executable)
|
||||||
|
|||||||
@@ -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/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.konan.target
|
||||||
|
|
||||||
|
enum class SanitizerKind {
|
||||||
|
ADDRESS,
|
||||||
|
THREAD,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user