TSan instrumentation of module code (#8474)
This implements TSan instrumentation in the IREE compiler, and adds a IREE_BYTECODE_MODULE_ENABLE_TSAN flag, which when set causes iree_bytecode_module to enable TSan instrumentation.
When IREE_BUILD_TESTS is on, we enforce early that IREE_ENABLE_TSAN and IREE_BYTECODE_MODULE_ENABLE_TSAN agree, as we would otherwise get test crashes anyway.
IREE_BYTECODE_MODULE_ENABLE_TSAN also requires IREE_BYTECODE_MODULE_FORCE_SYSTEM_DYLIB_LINKER to be set. That too is enforced early.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ed11ac..b320b20 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -126,6 +126,29 @@
option(IREE_ENABLE_ASAN "Enable address sanitizer" OFF)
option(IREE_ENABLE_MSAN "Enable memory sanitizer" OFF)
option(IREE_ENABLE_TSAN "Enable thread sanitizer" OFF)
+option(IREE_BYTECODE_MODULE_ENABLE_TSAN "Enable thread sanitizer in IREE modules in tests" OFF)
+
+# STREQUAL feels wrong here - we don't care about the exact true-value used,
+# ON or TRUE or something else. But we haven't been able to think of a less bad
+# alternative. https://github.com/google/iree/pull/8474#discussion_r840790062
+if(NOT IREE_ENABLE_TSAN STREQUAL IREE_BYTECODE_MODULE_ENABLE_TSAN)
+ message(SEND_ERROR
+ "IREE_ENABLE_TSAN and IREE_BYTECODE_MODULE_ENABLE_TSAN must be "
+ "simultaneously ON or OFF. "
+ "A discrepancy between the two would cause tests to crash as IREE "
+ "runtime code (controlled by IREE_ENABLE_TSAN) calls into test IREE "
+ "modules (controlled by IREE_BYTECODE_MODULE_ENABLE_TSAN)")
+endif()
+
+if(IREE_BYTECODE_MODULE_ENABLE_TSAN)
+ if(NOT IREE_BYTECODE_MODULE_FORCE_SYSTEM_DYLIB_LINKER)
+ message(SEND_ERROR
+ "When IREE_BYTECODE_MODULE_ENABLE_TSAN is ON, "
+ "IREE_BYTECODE_MODULE_FORCE_SYSTEM_DYLIB_LINKER must also be ON. "
+ "TSAN instrumentation is not currently supported in embedded modules.")
+ endif()
+endif()
+
option(IREE_ENABLE_CCACHE "Use ccache if installed to speed up rebuilds." OFF)
if(${IREE_ENABLE_CCACHE})
diff --git a/build_tools/cmake/iree_bytecode_module.cmake b/build_tools/cmake/iree_bytecode_module.cmake
index c4a9d1c..fedc2ba 100644
--- a/build_tools/cmake/iree_bytecode_module.cmake
+++ b/build_tools/cmake/iree_bytecode_module.cmake
@@ -116,10 +116,17 @@
# Note: -iree-llvm-system-linker-path is left unspecified.
endif()
- if (IREE_BYTECODE_MODULE_FORCE_SYSTEM_DYLIB_LINKER)
+ if(IREE_BYTECODE_MODULE_FORCE_SYSTEM_DYLIB_LINKER)
list(APPEND _ARGS "-iree-llvm-link-embedded=false")
endif()
+ # Support testing in TSan build dirs. Unlike other sanitizers, TSan is an
+ # ABI break: when the host code is built with TSan, the module must be too,
+ # otherwise we get crashes calling module code.
+ if(IREE_BYTECODE_MODULE_ENABLE_TSAN)
+ list(APPEND _ARGS "-iree-llvm-sanitize=thread")
+ endif()
+
# Depending on the binary instead of the target here given we might not have
# a target in this CMake invocation when cross-compiling.
add_custom_command(
diff --git a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMAOTTarget.cpp b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMAOTTarget.cpp
index 2818949..36a491c 100644
--- a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMAOTTarget.cpp
+++ b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMAOTTarget.cpp
@@ -284,6 +284,12 @@
function.addFnAttr(llvm::Attribute::SanitizeAddress);
}
} break;
+ case SanitizerKind::kThread: {
+ libraryBuilder.setSanitizerKind(LibraryBuilder::SanitizerKind::THREAD);
+ for (auto &function : llvmModule->getFunctionList()) {
+ function.addFnAttr(llvm::Attribute::SanitizeThread);
+ }
+ } break;
}
auto align16 = llvm::Attribute::getWithAlignment(context, llvm::Align(16));
for (auto entryPointOp :
diff --git a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMIRPasses.cpp b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMIRPasses.cpp
index 49da9da..7d184ed 100644
--- a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMIRPasses.cpp
+++ b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMIRPasses.cpp
@@ -20,6 +20,7 @@
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
namespace mlir {
namespace iree_compiler {
@@ -98,9 +99,19 @@
Opts, moduleUseAfterScope, useOdrIndicator));
});
} break;
+ case SanitizerKind::kThread: {
+ passBuilder.registerOptimizerLastEPCallback(
+ [](llvm::ModulePassManager &modulePassManager,
+ llvm::OptimizationLevel Level) {
+ modulePassManager.addPass(llvm::ModuleThreadSanitizerPass());
+ modulePassManager.addPass(llvm::createModuleToFunctionPassAdaptor(
+ llvm::ThreadSanitizerPass()));
+ });
+ } break;
}
- if (options.optLevel != llvm::OptimizationLevel::O0) {
+ if (options.optLevel != llvm::OptimizationLevel::O0 ||
+ options.sanitizerKind != SanitizerKind::kNone) {
llvm::ModulePassManager modulePassManager;
modulePassManager =
passBuilder.buildPerModuleDefaultPipeline(options.optLevel);
diff --git a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.cpp b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.cpp
index 7cdd79b..2534ea3 100644
--- a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.cpp
+++ b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.cpp
@@ -109,7 +109,9 @@
"iree-llvm-sanitize", llvm::cl::desc("Apply LLVM sanitize feature"),
llvm::cl::init(SanitizerKind::kNone),
llvm::cl::values(clEnumValN(SanitizerKind::kAddress, "address",
- "Address sanitizer support")));
+ "Address sanitizer support"),
+ clEnumValN(SanitizerKind::kThread, "thread",
+ "Thread sanitizer support")));
targetOptions.sanitizerKind = clSanitizerKind;
static llvm::cl::opt<std::string> clTargetABI(
diff --git a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.h b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.h
index 2a833db..bb51176 100644
--- a/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.h
+++ b/iree/compiler/Dialect/HAL/Target/LLVM/LLVMTargetOptions.h
@@ -20,6 +20,7 @@
enum class SanitizerKind {
kNone = 0,
kAddress,
+ kThread,
};
struct LLVMTargetOptions {
diff --git a/iree/hal/local/loaders/system_library_loader.c b/iree/hal/local/loaders/system_library_loader.c
index b18acc6..ebd0213 100644
--- a/iree/hal/local/loaders/system_library_loader.c
+++ b/iree/hal/local/loaders/system_library_loader.c
@@ -179,6 +179,18 @@
"runtime is not compiled with it enabled; add -fsanitize=address to "
"the runtime compilation options");
#endif // IREE_SANITIZER_ADDRESS
+#if defined(IREE_SANITIZER_THREAD)
+ case IREE_HAL_EXECUTABLE_LIBRARY_SANITIZER_THREAD:
+ // TSAN is compiled into the host and we can load this library.
+ break;
+#else
+ case IREE_HAL_EXECUTABLE_LIBRARY_SANITIZER_THREAD:
+ return iree_make_status(
+ IREE_STATUS_UNAVAILABLE,
+ "executable library is compiled with TSAN support but the host "
+ "runtime is not compiled with it enabled; add -fsanitize=thread to "
+ "the runtime compilation options");
+#endif // IREE_SANITIZER_THREAD
default:
return iree_make_status(
IREE_STATUS_UNAVAILABLE,