Skip to content

Add experimental support for Thread Sanitizer #2076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ ERROR(error_unsupported_target_arch, none,
ERROR(error_unsupported_opt_for_target, none,
"unsupported option '%0' for target '%1'", (StringRef, StringRef))

ERROR(error_argument_not_allowed_with, none,
"argument '%0' is not allowed with '%1'", (StringRef, StringRef))

ERROR(cannot_open_file,none,
"cannot open file '%0' (%1)", (StringRef, StringRef))
ERROR(cannot_open_serialized_file,none,
Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Sanitizers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace swift {
enum class SanitizerKind : unsigned {
None = 0,
Address,
Thread,
};

} // end namespace swift
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Runtime/Concurrent.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ template <class EntryTy> class ConcurrentMap {

// Try to set the edge to the new node.
if (std::atomic_compare_exchange_strong_explicit(edge, &node, newNode,
std::memory_order_release,
std::memory_order_acq_rel,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but I'm going to link to C++ DR 2445 in protest. :)

std::memory_order_acquire)) {
// If that succeeded, cache and report that we created a new node.
LastSearch.store(newNode, std::memory_order_release);
Expand Down
3 changes: 3 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,9 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
if (context.OI.SelectedSanitizer == SanitizerKind::Address)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this);

if (context.OI.SelectedSanitizer == SanitizerKind::Thread)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this);

if (context.Args.hasArg(options::OPT_embed_bitcode,
options::OPT_embed_bitcode_marker)) {
Arguments.push_back("-bitcode_bundle");
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,10 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM,
// FIXME: Technically should be "consume", but that introduces barriers in the
// current LLVM ARM backend.
auto load = IGF.Builder.CreateLoad(cache);
// Make this barrier explicit when building for TSan to avoid false positives.
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
load->setOrdering(llvm::AtomicOrdering::Acquire);


// Compare the load result against null.
auto isNullBB = IGF.createBasicBlock("cacheIsNull");
Expand Down
12 changes: 12 additions & 0 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
PM.add(createAddressSanitizerModulePass());
}

static void addThreadSanitizerPass(const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM) {
PM.add(createThreadSanitizerPass());
}

std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>>
swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) {
// Things that maybe we should collect from the command line:
Expand Down Expand Up @@ -152,6 +157,13 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module,
addAddressSanitizerPasses);
}

if (Opts.Sanitize == SanitizerKind::Thread) {
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
addThreadSanitizerPass);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
addThreadSanitizerPass);
}

// Configure the function passes.
legacy::FunctionPassManager FunctionPasses(Module);
FunctionPasses.add(createTargetTransformInfoWrapperPass(
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ IRGenFunction::IRGenFunction(IRGenModule &IGM,
// file or via annotations.
if (IGM.Opts.Sanitize == SanitizerKind::Address)
Fn->addFnAttr(llvm::Attribute::SanitizeAddress);
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
Fn->addFnAttr(llvm::Attribute::SanitizeThread);

emitPrologue();
}
Expand Down
49 changes: 40 additions & 9 deletions lib/Option/SanitizerOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
//===----------------------------------------------------------------------===//

#include "swift/Option/SanitizerOptions.h"
#include "swift/Basic/Platform.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "llvm/ADT/StringSwitch.h"
using namespace swift;

static StringRef toStringRef(const SanitizerKind kind) {
switch (kind) {
case SanitizerKind::Address:
return "address";
case SanitizerKind::Thread:
return "thread";
case SanitizerKind::None:
llvm_unreachable("Getting a name for SanitizerKind::None");
}
Expand All @@ -36,23 +40,50 @@ SanitizerKind swift::parseSanitizerArgValues(const llvm::opt::Arg *A,
SanitizerKind kind = SanitizerKind::None;

// Find the sanitizer kind.
// TODO: Add support for dealing with multiple sanitizers.
SanitizerKind pKind = SanitizerKind::None;
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
const char *Value = A->getValue(i);
if (StringRef(Value).equals("address")) {
kind = SanitizerKind::Address;
} else {
kind =
llvm::StringSwitch<SanitizerKind>(A->getValue(i))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird indentation here.

.Case("address", SanitizerKind::Address)
.Case("thread", SanitizerKind::Thread)
.Default(SanitizerKind::None);

if (kind == SanitizerKind::None) {
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
A->getOption().getName(), A->getValue(i));
A->getOption().getPrefixedName(), A->getValue(i));
return kind;
}

// Currently, more than one sanitizer cannot be enabled at the same time.
if (pKind != SanitizerKind::None && pKind != kind) {
SmallString<128> pb;
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
(A->getOption().getPrefixedName() + toStringRef(pKind)).toStringRef(pb),
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b));
}
pKind = kind;
}

if (kind == SanitizerKind::None)
return kind;

// Check if the target is supported for this sanitizer.
if (!Triple.isOSDarwin() && kind != SanitizerKind::None) {
SmallVector<char, 128> buffer;
// None of the sanitizers work on Linux right now.
if (!Triple.isOSDarwin()) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
Triple.getTriple());
}
// Thread Sanitizer only works on OS X and the simulators. It's only supported
// on 64 bit arcitectures.
if (kind == SanitizerKind::Thread &&
(!(Triple.isMacOSX() || tripleIsAnySimulator(Triple)) ||
!Triple.isArch64Bit())) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getName() + toStringRef(kind)).toStringRef(buffer),
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
Triple.getTriple());
}

Expand Down
4 changes: 3 additions & 1 deletion stdlib/public/core/Arrays.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,10 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol {
// unnecessary uniqueness check. We disable inlining here to curb code
// growth.
_buffer = ${Self}._allocateBufferUninitialized(minimumCapacity: count)
_buffer.count = count
}
_buffer.count = count
// Can’t store count here because the buffer might be pointing to the
// shared empty array.
}

/// Entry point for `Array` literal construction; builds and returns
Expand Down
3 changes: 1 addition & 2 deletions stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,10 +560,9 @@ swift::swift_conformsToProtocol(const Metadata *type,
return FoundConformance.first;
}

unsigned failedGeneration = ConformanceCacheGeneration;

// If we didn't have an up-to-date cache entry, scan the conformance records.
C.SectionsToScanLock.lock();
unsigned failedGeneration = ConformanceCacheGeneration;

// If we have no new information to pull in (and nobody else pulled in
// new information while we waited on the lock), we're done.
Expand Down
65 changes: 44 additions & 21 deletions test/Driver/sanitizers.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=OSX %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOSSIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOS %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS %s

// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=LINUX %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_OSX %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOSSIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=ASAN_LINUX %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_OSX %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86-apple-macosx10.9 %s 2>&1 | FileCheck -check-prefix=TSAN_OSX_32 %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_IOSSIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-ios7.1 %s 2>&1 | FileCheck -check-prefix=TSAN_IOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_tvOS_SIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-tvos9.0 %s 2>&1 | FileCheck -check-prefix=TSAN_tvOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target i386-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS_SIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target armv7k-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=TSAN_LINUX %s

// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize=unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,thread %s 2>&1 | FileCheck -check-prefix=INCOMPATIBLESANITIZERS %s

// ASAN: swift
// ASAN: -sanitize=address

// OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
// IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
// IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
// tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
// tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
// watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
// watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
// LINUX: unsupported option 'sanitize=address' for target 'x86_64-unknown-linux-gnu'
// ASAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
// ASAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
// ASAN_IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
// ASAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
// ASAN_tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
// ASAN_watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
// ASAN_watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
// ASAN_LINUX: unsupported option '-sanitize=address' for target 'x86_64-unknown-linux-gnu'

// ASAN: -rpath @executable_path

// BADARG: unsupported argument 'unknown' to option 'sanitize='
// TSAN: swift
// TSAN: -sanitize=thread

// TSAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.tsan_osx_dynamic.dylib
// TSAN_OSX_32: unsupported option '-sanitize=thread' for target 'x86-apple-macosx10.9'
// TSAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_iossim_dynamic.dylib
// TSAN_IOS: unsupported option '-sanitize=thread' for target 'arm64-apple-ios7.1'
// TSAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_tvossim_dynamic.dylib
// TSAN_tvOS: unsupported option '-sanitize=thread' for target 'arm64-apple-tvos9.0'
// TSAN_watchOS_SIM: unsupported option '-sanitize=thread' for target 'i386-apple-watchos2.0'
// TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0'
// TSAN_LINUX: unsupported option '-sanitize=thread' for target 'x86_64-unknown-linux-gnu'

// TSAN: -rpath @executable_path

// BADARG: unsupported argument 'unknown' to option '-sanitize='
// INCOMPATIBLESANITIZERS: argument '-sanitize=address' is not allowed with '-sanitize=thread'
2 changes: 2 additions & 0 deletions test/IRGen/sanitizer-attributes.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// This test verifies that we add the function attributes used by sanitizers.

// RUN: %target-swift-frontend -emit-ir -sanitize=address %s | FileCheck %s -check-prefix=ASAN
// RUN: %target-swift-frontend -emit-ir -target x86_64-apple-macosx10.9 -sanitize=thread %s | FileCheck %s -check-prefix=TSAN

// XFAIL: linux

func test() {
}

// ASAN: Function Attrs: sanitize_address
// TSAN: Function Attrs: sanitize_thread