Skip to content

Commit 701752c

Browse files
authored
Merge pull request #9431 from rjmccall/dynamic-exclusivity-refinements
Dynamic exclusivity refinements
2 parents e987d04 + 93243e5 commit 701752c

File tree

8 files changed

+192
-52
lines changed

8 files changed

+192
-52
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,12 +570,28 @@ class FieldType {
570570
enum class ExclusivityFlags : uintptr_t {
571571
Read = 0x0,
572572
Modify = 0x1,
573-
ActionMask = 0x1
573+
// Leave space for other actions.
574+
// Don't rely on ActionMask in stable ABI.
575+
ActionMask = 0x1,
576+
577+
// Downgrade exclusivity failures to a warning.
578+
WarningOnly = 0x10
574579
};
580+
static inline ExclusivityFlags operator|(ExclusivityFlags lhs,
581+
ExclusivityFlags rhs) {
582+
return ExclusivityFlags(uintptr_t(lhs) | uintptr_t(rhs));
583+
}
584+
static inline ExclusivityFlags &operator|=(ExclusivityFlags &lhs,
585+
ExclusivityFlags rhs) {
586+
return (lhs = (lhs | rhs));
587+
}
575588
static inline ExclusivityFlags getAccessAction(ExclusivityFlags flags) {
576589
return ExclusivityFlags(uintptr_t(flags)
577590
& uintptr_t(ExclusivityFlags::ActionMask));
578591
}
592+
static inline bool isWarningOnly(ExclusivityFlags flags) {
593+
return uintptr_t(flags) & uintptr_t(ExclusivityFlags::WarningOnly);
594+
}
579595

580596
} // end namespace swift
581597

include/swift/Runtime/Exclusivity.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ struct ValueBuffer;
2828
///
2929
/// The buffer is opaque scratch space that the runtime may use for
3030
/// the duration of the access.
31+
///
32+
/// The PC argument is an instruction pointer to associate with the start
33+
/// of the access. If it is null, the return address of the call to
34+
/// swift_beginAccess will be used.
3135
SWIFT_RUNTIME_EXPORT
3236
void swift_beginAccess(void *pointer, ValueBuffer *buffer,
33-
ExclusivityFlags flags);
37+
ExclusivityFlags flags, void *pc);
3438

3539
/// Stop dynamically tracking an access.
3640
SWIFT_RUNTIME_EXPORT

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ FUNCTION(RegisterTypeMetadataRecords,
10921092
// void swift_beginAccess(void *pointer, ValueBuffer *scratch, size_t flags);
10931093
FUNCTION(BeginAccess, swift_beginAccess, DefaultCC,
10941094
RETURNS(VoidTy),
1095-
ARGS(Int8PtrTy, getFixedBufferTy()->getPointerTo(), SizeTy),
1095+
ARGS(Int8PtrTy, getFixedBufferTy()->getPointerTo(), SizeTy, Int8PtrTy),
10961096
ATTRS(NoUnwind))
10971097

10981098
// void swift_endAccess(ValueBuffer *scratch);

lib/IRGen/IRGenSIL.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3952,7 +3952,7 @@ void IRGenSILFunction::visitProjectBoxInst(swift::ProjectBoxInst *i) {
39523952
}
39533953
}
39543954

3955-
static ExclusivityFlags getExclusivityFlags(SILAccessKind kind) {
3955+
static ExclusivityFlags getExclusivityAction(SILAccessKind kind) {
39563956
switch (kind) {
39573957
case SILAccessKind::Read:
39583958
return ExclusivityFlags::Read;
@@ -3965,9 +3965,20 @@ static ExclusivityFlags getExclusivityFlags(SILAccessKind kind) {
39653965
llvm_unreachable("bad access kind");
39663966
}
39673967

3968+
static ExclusivityFlags getExclusivityFlags(SILModule &M,
3969+
SILAccessKind kind) {
3970+
auto flags = getExclusivityAction(kind);
3971+
3972+
// In old Swift compatibility modes, downgrade this to a warning.
3973+
if (M.getASTContext().LangOpts.isSwiftVersion3())
3974+
flags |= ExclusivityFlags::WarningOnly;
3975+
3976+
return flags;
3977+
}
3978+
39683979
template <class Inst>
39693980
static ExclusivityFlags getExclusivityFlags(Inst *i) {
3970-
return getExclusivityFlags(i->getAccessKind());
3981+
return getExclusivityFlags(i->getModule(), i->getAccessKind());
39713982
}
39723983

39733984
void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
@@ -3992,8 +4003,9 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
39924003
Builder.CreateBitCast(addr.getAddress(), IGM.Int8PtrTy);
39934004
llvm::Value *flags =
39944005
llvm::ConstantInt::get(IGM.SizeTy, uint64_t(getExclusivityFlags(access)));
4006+
llvm::Value *pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
39954007
auto call = Builder.CreateCall(IGM.getBeginAccessFn(),
3996-
{ pointer, scratch, flags });
4008+
{ pointer, scratch, flags, pc });
39974009
call->setDoesNotThrow();
39984010

39994011
setLoweredDynamicallyEnforcedAddress(access, addr, scratch);
@@ -4003,6 +4015,11 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
40034015
llvm_unreachable("bad access enforcement");
40044016
}
40054017

4018+
static bool hasBeenInlined(BeginUnpairedAccessInst *access) {
4019+
// Check to see if the buffer is defined locally.
4020+
return isa<AllocStackInst>(access->getBuffer());
4021+
}
4022+
40064023
void IRGenSILFunction::visitBeginUnpairedAccessInst(
40074024
BeginUnpairedAccessInst *access) {
40084025
Address addr = getLoweredAddress(access->getSource());
@@ -4022,8 +4039,26 @@ void IRGenSILFunction::visitBeginUnpairedAccessInst(
40224039
Builder.CreateBitCast(addr.getAddress(), IGM.Int8PtrTy);
40234040
llvm::Value *flags =
40244041
llvm::ConstantInt::get(IGM.SizeTy, uint64_t(getExclusivityFlags(access)));
4042+
4043+
// Compute the effective PC of the access.
4044+
// Since begin_unpaired_access is designed for materializeForSet, our
4045+
// heuristic here is as well: we've either been inlined, in which case
4046+
// we should use the current PC (i.e. pass null), or we haven't,
4047+
// in which case we should use the caller, which is generally ok because
4048+
// materializeForSet can't usually be thunked.
4049+
llvm::Value *pc;
4050+
if (hasBeenInlined(access)) {
4051+
pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
4052+
} else {
4053+
auto retAddrFn =
4054+
llvm::Intrinsic::getDeclaration(IGM.getModule(),
4055+
llvm::Intrinsic::returnaddress);
4056+
pc = Builder.CreateCall(retAddrFn,
4057+
{ llvm::ConstantInt::get(IGM.Int32Ty, 0) });
4058+
}
4059+
40254060
auto call = Builder.CreateCall(IGM.getBeginAccessFn(),
4026-
{ pointer, scratch, flags });
4061+
{ pointer, scratch, flags, pc });
40274062
call->setDoesNotThrow();
40284063
return;
40294064
}

stdlib/public/runtime/Exclusivity.cpp

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/Runtime/Exclusivity.h"
2020
#include "swift/Runtime/Metadata.h"
2121
#include <memory>
22+
#include <stdio.h>
2223

2324
// Pick an implementation strategy.
2425
#ifndef SWIFT_EXCLUSIVITY_USE_THREADLOCAL
@@ -41,6 +42,18 @@
4142
#include <stdio.h>
4243
#endif
4344

45+
// Pick a return-address strategy
46+
#if __GNUC__
47+
#define get_return_address() __builtin_return_address(0)
48+
#elif _MSC_VER
49+
#include <intrin.h>
50+
#define get_return_address() _ReturnAddress()
51+
#else
52+
#error missing implementation for get_return_address
53+
#define get_return_address() ((void*) 0)
54+
#endif
55+
56+
4457
using namespace swift;
4558

4659
bool swift::_swift_disableExclusivityChecking = false;
@@ -49,40 +62,70 @@ static const char *getAccessName(ExclusivityFlags flags) {
4962
switch (flags) {
5063
case ExclusivityFlags::Read: return "read";
5164
case ExclusivityFlags::Modify: return "modify";
65+
default: return "unknown";
66+
}
67+
}
68+
69+
static void reportExclusivityConflict(ExclusivityFlags oldAction, void *oldPC,
70+
ExclusivityFlags newFlags, void *newPC,
71+
void *pointer) {
72+
// TODO: print something about where the pointer came from?
73+
// TODO: if we do something more computationally intense here,
74+
// suppress warnings if the total warning count exceeds some limit
75+
76+
if (isWarningOnly(newFlags)) {
77+
fprintf(stderr,
78+
"WARNING: %s/%s access conflict detected on address %p\n"
79+
" first access started at PC=%p\n"
80+
" second access started at PC=%p\n",
81+
getAccessName(oldAction),
82+
getAccessName(getAccessAction(newFlags)),
83+
pointer, oldPC, newPC);
84+
return;
5285
}
53-
return "unknown";
86+
87+
// TODO: try to recover source-location information from the return
88+
// address.
89+
fatalError(0,
90+
"%s/%s access conflict detected on address %p, aborting\n"
91+
" first access started at PC=%p\n"
92+
" second access started at PC=%p\n",
93+
getAccessName(oldAction),
94+
getAccessName(getAccessAction(newFlags)),
95+
pointer, oldPC, newPC);
5496
}
5597

5698
namespace {
5799

58100
/// A single access that we're tracking.
59101
struct Access {
60102
void *Pointer;
61-
uintptr_t HereAndAction;
62-
Access *Next;
103+
void *PC;
104+
uintptr_t NextAndAction;
63105

64106
enum : uintptr_t {
65107
ActionMask = (uintptr_t)ExclusivityFlags::ActionMask,
66-
HereMask = ~ActionMask
108+
NextMask = ~ActionMask
67109
};
68110

69-
Access **getHere() const {
70-
return reinterpret_cast<Access**>(HereAndAction & HereMask);
111+
Access *getNext() const {
112+
return reinterpret_cast<Access*>(NextAndAction & NextMask);
71113
}
72114

73-
void setHere(Access **newHere) {
74-
HereAndAction = reinterpret_cast<uintptr_t>(newHere) | uintptr_t(getAccessAction());
115+
void setNext(Access *next) {
116+
NextAndAction =
117+
reinterpret_cast<uintptr_t>(next) | (NextAndAction & NextMask);
75118
}
76119

77120
ExclusivityFlags getAccessAction() const {
78-
return ExclusivityFlags(HereAndAction & ActionMask);
121+
return ExclusivityFlags(NextAndAction & ActionMask);
79122
}
80123

81-
void initialize(void *pointer, Access **here, ExclusivityFlags action) {
82-
assert(*here == nullptr && "not inserting to end of list");
124+
void initialize(void *pc, void *pointer, Access *next,
125+
ExclusivityFlags action) {
83126
Pointer = pointer;
84-
HereAndAction = reinterpret_cast<uintptr_t>(here) | uintptr_t(action);
85-
Next = nullptr;
127+
PC = pc;
128+
NextAndAction = reinterpret_cast<uintptr_t>(next) | uintptr_t(action);
86129
}
87130
};
88131

@@ -96,11 +139,10 @@ class AccessSet {
96139
public:
97140
constexpr AccessSet() {}
98141

99-
void insert(Access *access, void *pointer, ExclusivityFlags flags) {
142+
void insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags) {
100143
auto action = getAccessAction(flags);
101144

102-
Access **curP = &Head;
103-
for (Access *cur = *curP; cur != nullptr; curP = &cur->Next, cur = *curP) {
145+
for (Access *cur = Head; cur != nullptr; cur = cur->getNext()) {
104146
// Ignore accesses to different values.
105147
if (cur->Pointer != pointer)
106148
continue;
@@ -111,22 +153,34 @@ class AccessSet {
111153
continue;
112154

113155
// Otherwise, it's a conflict.
114-
// TODO: try to recover source-location information from the return
115-
// address.
116-
fatalError(0, "%s/%s access conflict detected on address %p, aborting\n",
117-
getAccessName(action), getAccessName(cur->getAccessAction()),
118-
pointer);
156+
reportExclusivityConflict(cur->getAccessAction(), cur->PC,
157+
flags, pc, pointer);
158+
159+
// If we're only warning, don't report multiple conflicts.
160+
break;
119161
}
120162

121-
access->initialize(pointer, curP, action);
122-
*curP = access;
163+
// Insert to the front of the array so that remove tends to find it faster.
164+
access->initialize(pc, pointer, Head, action);
165+
Head = access;
123166
}
124167

125-
static void remove(Access *access) {
126-
Access **here = access->getHere();
127-
*here = access->Next;
128-
if (access->Next != nullptr)
129-
access->Next->setHere(here);
168+
void remove(Access *access) {
169+
auto cur = Head;
170+
// Fast path: stack discipline.
171+
if (cur == access) {
172+
Head = cur->getNext();
173+
return;
174+
}
175+
176+
for (Access *last = cur; cur != nullptr; last = cur, cur = cur->getNext()) {
177+
if (last == access) {
178+
last->setNext(cur->getNext());
179+
return;
180+
}
181+
}
182+
183+
swift_runtime_unreachable("access not found in set");
130184
}
131185
};
132186

@@ -205,7 +259,7 @@ static AccessSet &getAccessSet(void *pointer) {
205259
/// This may cause a runtime failure if an incompatible access is
206260
/// already underway.
207261
void swift::swift_beginAccess(void *pointer, ValueBuffer *buffer,
208-
ExclusivityFlags flags) {
262+
ExclusivityFlags flags, void *pc) {
209263
assert(pointer && "beginning an access on a null pointer?");
210264

211265
Access *access = reinterpret_cast<Access*>(buffer);
@@ -217,7 +271,9 @@ void swift::swift_beginAccess(void *pointer, ValueBuffer *buffer,
217271
return;
218272
}
219273

220-
getAccessSet(pointer).insert(access, pointer, flags);
274+
if (!pc) pc = get_return_address();
275+
276+
getAccessSet(pointer).insert(access, pc, pointer, flags);
221277
}
222278

223279
/// End tracking a dynamic access.
@@ -231,5 +287,5 @@ void swift::swift_endAccess(ValueBuffer *buffer) {
231287
return;
232288
}
233289

234-
AccessSet::remove(access);
290+
getAccessSet(pointer).remove(access);
235291
}

0 commit comments

Comments
 (0)