[K/N] Basic support of thread sanitizer for generated code

This commit is contained in:
Pavel Kunyavskiy
2022-07-25 19:15:05 +02:00
committed by Space
parent 6efe416258
commit 51651aef74
8 changed files with 54 additions and 25 deletions
@@ -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>(
@@ -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(
@@ -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()
@@ -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() {
@@ -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")
}
@@ -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