[K/N] Basic support of thread sanitizer for generated code
This commit is contained in:
+3
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.konan.target.SanitizerKind
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
@@ -37,6 +38,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
||||
val bundleVersion by stringOption()
|
||||
|
||||
val appStateTracking by option<AppStateTracking>()
|
||||
|
||||
val sanitizer by option<SanitizerKind>()
|
||||
}
|
||||
|
||||
open class BinaryOption<T : Any>(
|
||||
|
||||
+17
-1
@@ -60,6 +60,19 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
?: target.family.isAppleFamily // Default is true for Apple targets.
|
||||
val generateDebugTrampoline = debug && configuration.get(KonanConfigKeys.GENERATE_DEBUG_TRAMPOLINE) ?: false
|
||||
val optimizationsEnabled = configuration.getBoolean(KonanConfigKeys.OPTIMIZATION)
|
||||
val sanitizer = configuration.get(BinaryOptions.sanitizer)?.takeIf {
|
||||
when {
|
||||
it != SanitizerKind.THREAD -> "${it.name} sanitizer is not supported yet"
|
||||
produce == CompilerOutputKind.STATIC -> "${it.name} sanitizer is unsupported for static library"
|
||||
produce == CompilerOutputKind.FRAMEWORK && produceStaticFramework -> "${it.name} sanitizer is unsupported for static framework"
|
||||
it !in target.supportedSanitizers() -> "${it.name} sanitizer is unsupported on ${target.name}"
|
||||
else -> null
|
||||
}?.let {message ->
|
||||
configuration.report(CompilerMessageSeverity.STRONG_WARNING, message)
|
||||
return@takeIf false
|
||||
}
|
||||
return@takeIf true
|
||||
}
|
||||
|
||||
private val defaultMemoryModel get() =
|
||||
if (target.supportsThreads()) {
|
||||
@@ -226,7 +239,9 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
private val shouldCoverLibraries = !configuration.getList(KonanConfigKeys.LIBRARIES_TO_COVER).isNullOrEmpty()
|
||||
|
||||
private val defaultAllocationMode get() = when {
|
||||
memoryModel == MemoryModel.EXPERIMENTAL && target.supportsMimallocAllocator() -> AllocationMode.MIMALLOC
|
||||
memoryModel == MemoryModel.EXPERIMENTAL && target.supportsMimallocAllocator() && sanitizer == null -> {
|
||||
AllocationMode.MIMALLOC
|
||||
}
|
||||
else -> AllocationMode.STD
|
||||
}
|
||||
|
||||
@@ -364,6 +379,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
}
|
||||
freezing != defaultFreezing -> "with ${freezing.name.replaceFirstChar { it.lowercase() }} freezing mode"
|
||||
runtimeAssertsMode != RuntimeAssertsMode.IGNORE -> "with runtime assertions"
|
||||
sanitizer != null -> "with sanitizers enabled"
|
||||
else -> null
|
||||
}
|
||||
CacheSupport(
|
||||
|
||||
+3
-1
@@ -180,7 +180,9 @@ internal class Linker(val context: Context) {
|
||||
kind = linkerOutput,
|
||||
outputDsymBundle = context.config.outputFiles.symbolicInfoFile,
|
||||
needsProfileLibrary = needsProfileLibrary,
|
||||
mimallocEnabled = mimallocEnabled)
|
||||
mimallocEnabled = mimallocEnabled,
|
||||
sanitizer = context.config.sanitizer
|
||||
)
|
||||
(linkerInput.preLinkCommands + finalOutputCommands).forEach {
|
||||
it.logWith(context::log)
|
||||
it.execute()
|
||||
|
||||
+15
-1
@@ -53,6 +53,7 @@ data class LlvmPipelineConfig(
|
||||
val makeDeclarationsHidden: Boolean,
|
||||
val objCPasses: Boolean,
|
||||
val inlineThreshold: Int?,
|
||||
val sanitizer: SanitizerKind?,
|
||||
)
|
||||
|
||||
private fun getCpuModel(context: Context): String {
|
||||
@@ -100,7 +101,8 @@ internal fun createLTOPipelineConfigForRuntime(context: Context): LlvmPipelineCo
|
||||
internalize = false,
|
||||
objCPasses = configurables is AppleConfigurables,
|
||||
makeDeclarationsHidden = false,
|
||||
inlineThreshold = tryGetInlineThreshold(context)
|
||||
inlineThreshold = tryGetInlineThreshold(context),
|
||||
sanitizer = null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -171,6 +173,7 @@ internal fun createLTOFinalPipelineConfig(context: Context): LlvmPipelineConfig
|
||||
makeDeclarationsHidden,
|
||||
objcPasses,
|
||||
inlineThreshold,
|
||||
context.config.sanitizer
|
||||
)
|
||||
}
|
||||
|
||||
@@ -238,6 +241,17 @@ class LlvmOptimizationPipeline(
|
||||
// TODO: Consider adding other ObjC passes.
|
||||
LLVMAddObjCARCContractPass(modulePasses)
|
||||
}
|
||||
|
||||
when (config.sanitizer) {
|
||||
null -> {}
|
||||
SanitizerKind.ADDRESS -> TODO("Address sanitizer is not supported yet")
|
||||
SanitizerKind.THREAD -> {
|
||||
getFunctions(llvmModule)
|
||||
.filter { LLVMIsDeclaration(it) == 0 }
|
||||
.forEach { addLlvmFunctionEnumAttribute(it, LlvmFunctionAttribute.SanitizeThread) }
|
||||
LLVMAddThreadSanitizerPass(modulePasses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun run() {
|
||||
|
||||
+1
@@ -113,4 +113,5 @@ sealed class LlvmFunctionAttribute(private val llvmAttributeName: String) : Llvm
|
||||
object NoUnwind : LlvmFunctionAttribute("nounwind")
|
||||
object NoReturn : LlvmFunctionAttribute("noreturn")
|
||||
object NoInline : LlvmFunctionAttribute("noinline")
|
||||
object SanitizeThread : LlvmFunctionAttribute("sanitize_thread")
|
||||
}
|
||||
|
||||
+8
-22
@@ -359,25 +359,8 @@ fun parseBitcodeFile(path: String): LLVMModuleRef = memScoped {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, LlvmFunctionAttribute.NoUnwind.asAttributeKindId().value)
|
||||
return attribute != null
|
||||
}
|
||||
|
||||
@@ -392,15 +375,15 @@ internal fun getLlvmAttributeKindId(attributeName: String): LLVMAttributeKindId
|
||||
data class LLVMAttributeKindId(val value: Int)
|
||||
|
||||
fun setFunctionNoUnwind(function: LLVMValueRef) {
|
||||
addLlvmFunctionEnumAttribute(function, nounwindAttrKindId)
|
||||
addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoUnwind)
|
||||
}
|
||||
|
||||
fun setFunctionNoReturn(function: LLVMValueRef) {
|
||||
addLlvmFunctionEnumAttribute(function, noreturnAttrKindId)
|
||||
addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoReturn)
|
||||
}
|
||||
|
||||
fun setFunctionNoInline(function: LLVMValueRef) {
|
||||
addLlvmFunctionEnumAttribute(function, noinlineAttrKindId)
|
||||
addLlvmFunctionEnumAttribute(function, LlvmFunctionAttribute.NoInline)
|
||||
}
|
||||
|
||||
internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attrKindId: LLVMAttributeKindId, value: Long = 0) {
|
||||
@@ -408,6 +391,9 @@ internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attrKindId: LL
|
||||
addLlvmFunctionAttribute(function, attribute)
|
||||
}
|
||||
|
||||
internal fun addLlvmFunctionEnumAttribute(function: LLVMValueRef, attr: LlvmAttribute, value: Long = 0) =
|
||||
addLlvmFunctionEnumAttribute(function, attr.asAttributeKindId(), value)
|
||||
|
||||
internal fun createLlvmEnumAttribute(llvmContext: LLVMContextRef, attrKindId: LLVMAttributeKindId, value: Long = 0) =
|
||||
LLVMCreateEnumAttribute(llvmContext, attrKindId.value, value)!!
|
||||
|
||||
@@ -417,7 +403,7 @@ internal fun addLlvmFunctionAttribute(function: LLVMValueRef, attribute: LLVMAtt
|
||||
|
||||
fun addFunctionSignext(function: LLVMValueRef, index: Int, type: LLVMTypeRef?) {
|
||||
if (type == int1Type || type == int8Type || type == int16Type) {
|
||||
val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, signextAttrKindId)
|
||||
val attribute = createLlvmEnumAttribute(LLVMGetTypeContext(function.type)!!, LlvmParameterAttribute.SignExt.asAttributeKindId())
|
||||
LLVMAddAttributeAtIndex(function, index, attribute)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <llvm/IR/LegacyPassManager.h>
|
||||
#include <llvm/Transforms/ObjCARC.h>
|
||||
#include <llvm/Transforms/Utils/Cloning.h>
|
||||
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@@ -52,4 +53,8 @@ void LLVMSetNoTailCall(LLVMValueRef Call) {
|
||||
int LLVMInlineCall(LLVMValueRef call) {
|
||||
InlineFunctionInfo IFI;
|
||||
return InlineFunction(*unwrap<CallBase>(call), IFI).isSuccess();
|
||||
}
|
||||
|
||||
void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM) {
|
||||
unwrap(PM)->add(createThreadSanitizerLegacyPassPass());
|
||||
}
|
||||
@@ -25,6 +25,8 @@ void LLVMSetNoTailCall(LLVMValueRef Call);
|
||||
|
||||
int LLVMInlineCall(LLVMValueRef call);
|
||||
|
||||
void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
Reference in New Issue
Block a user