Skip to content

Commit d0d8027

Browse files
authored
Merge pull request swiftlang#35579 from devincoughlin/tsan-empty-types
2 parents 00a43b2 + 38503da commit d0d8027

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

lib/IRGen/GenBuiltin.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ getLoweredTypeAndTypeInfo(IRGenModule &IGM, Type unloweredType) {
120120
/// emitBuiltinCall - Emit a call to a builtin function.
121121
void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
122122
Identifier FnId, SILType resultType,
123+
ArrayRef<SILType> argTypes,
123124
Explosion &args, Explosion &out,
124125
SubstitutionMap substitutions) {
125126
if (Builtin.ID == BuiltinValueKind::COWBufferForReading) {
@@ -1074,7 +1075,18 @@ if (Builtin.ID == BuiltinValueKind::id) { \
10741075

10751076
if (Builtin.ID == BuiltinValueKind::TSanInoutAccess) {
10761077
auto address = args.claimNext();
1077-
IGF.emitTSanInoutAccessCall(address);
1078+
1079+
// The tsanInoutAccess builtin takes a single argument, the address
1080+
// of the accessed storage
1081+
SILType accessedType = argTypes[0];
1082+
1083+
// Empty types (such as structs without stored properties) have a
1084+
// meaningless value for their address. We not should call into the
1085+
// TSan runtime to check for data races on accesses on such addresses.
1086+
if (!IGF.IGM.getTypeInfo(accessedType)
1087+
.isKnownEmpty(ResilienceExpansion::Maximal)) {
1088+
IGF.emitTSanInoutAccessCall(address);
1089+
}
10781090
return;
10791091
}
10801092

lib/IRGen/GenBuiltin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace irgen {
3333
/// Emit a call to a builtin function.
3434
void emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &builtin,
3535
Identifier fnId, SILType resultType,
36+
ArrayRef<SILType> argTypes,
3637
Explosion &args, Explosion &result,
3738
SubstitutionMap substitutions);
3839

lib/IRGen/IRGenSIL.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2689,18 +2689,21 @@ void IRGenSILFunction::visitBuiltinInst(swift::BuiltinInst *i) {
26892689

26902690
auto argValues = i->getArguments();
26912691
Explosion args;
2692+
SmallVector<SILType, 4> argTypes;
26922693

26932694
for (auto idx : indices(argValues)) {
26942695
auto argValue = argValues[idx];
26952696

26962697
// Builtin arguments should never be substituted, so use the value's type
26972698
// as the parameter type.
26982699
emitApplyArgument(*this, argValue, argValue->getType(), args);
2700+
2701+
argTypes.push_back(argValue->getType());
26992702
}
27002703

27012704
Explosion result;
27022705
emitBuiltinCall(*this, builtin, i->getName(), i->getType(),
2703-
args, result, i->getSubstitutions());
2706+
argTypes, args, result, i->getSubstitutions());
27042707

27052708
setLoweredExplosion(i, result);
27062709
}

test/SILGen/tsan_instrumentation.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// REQUIRES: tsan_runtime
22
// RUN: %target-swift-emit-silgen -sanitize=thread %s | %FileCheck %s
3+
// RUN: %target-swift-frontend -sanitize=thread -emit-ir -primary-file %s | %FileCheck --check-prefix=CHECK-LLVM-IR %s
34

45
// TSan is only supported on 64 bit.
56
// REQUIRES: PTRSIZE=64
@@ -63,3 +64,53 @@ func inoutGlobalClassStoredProperty() {
6364
// temporary buffer passed to takesInout().
6465
takesInout(&gClass.storedProperty)
6566
}
67+
68+
// Known-empty types don't have storage, so there is no address
69+
// to pass to the TSan runtime to check for data races on inout accesses.
70+
// In this case, the instrumentation should skip the call to
71+
// __tsan_external_write()
72+
73+
struct ZeroSizedStruct {
74+
mutating
75+
func mutate() { }
76+
}
77+
78+
struct NonEmptyStruct {
79+
var f: Int = 5
80+
mutating
81+
func mutate() { }
82+
}
83+
84+
// CHECK-LLVM-IR-LABEL: testNoInstrumentZeroSizedStruct
85+
func testNoInstrumentZeroSizedStruct() {
86+
var s = ZeroSizedStruct()
87+
88+
// CHECK-LLVM-IR-NOT: tsan_external_write
89+
s.mutate()
90+
}
91+
92+
func takesInout<T>(_ p: inout T) { }
93+
94+
// CHECK-LLVM-IR-LABEL: testNoInstrumentEmptyTuple
95+
func testNoInstrumentEmptyTuple() {
96+
var t: Void = ()
97+
98+
// CHECK-LLVM-IR-NOT: tsan_external_write
99+
takesInout(&t)
100+
}
101+
102+
// CHECK-LLVM-IR-LABEL: testNoInstrumentMutateInoutZeroSizedStruct
103+
func testNoInstrumentMutateInoutZeroSizedStruct(p: inout ZeroSizedStruct) {
104+
105+
// CHECK-LLVM-IR-NOT: tsan_external_write
106+
p.mutate()
107+
}
108+
109+
// CHECK-LLVM-IR-LABEL: testInstrumentNonEmptyStruct
110+
func testInstrumentNonEmptyStruct() {
111+
// Make sure we actually instrument accesses to non-empty structs.
112+
var s = NonEmptyStruct()
113+
114+
// CHECK-LLVM-IR: tsan_external_write
115+
s.mutate()
116+
}

0 commit comments

Comments
 (0)