Skip to content

Commit e01c31a

Browse files
committed
Remember a PC in the exclusivity-tracking set and report it during failures.
1 parent 2525710 commit e01c31a

File tree

6 files changed

+90
-17
lines changed

6 files changed

+90
-17
lines changed

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: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,8 +3992,9 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
39923992
Builder.CreateBitCast(addr.getAddress(), IGM.Int8PtrTy);
39933993
llvm::Value *flags =
39943994
llvm::ConstantInt::get(IGM.SizeTy, uint64_t(getExclusivityFlags(access)));
3995+
llvm::Value *pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
39953996
auto call = Builder.CreateCall(IGM.getBeginAccessFn(),
3996-
{ pointer, scratch, flags });
3997+
{ pointer, scratch, flags, pc });
39973998
call->setDoesNotThrow();
39983999

39994000
setLoweredDynamicallyEnforcedAddress(access, addr, scratch);
@@ -4003,6 +4004,11 @@ void IRGenSILFunction::visitBeginAccessInst(BeginAccessInst *access) {
40034004
llvm_unreachable("bad access enforcement");
40044005
}
40054006

4007+
static bool hasBeenInlined(BeginUnpairedAccessInst *access) {
4008+
// Check to see if the buffer is defined locally.
4009+
return isa<AllocStackInst>(access->getBuffer());
4010+
}
4011+
40064012
void IRGenSILFunction::visitBeginUnpairedAccessInst(
40074013
BeginUnpairedAccessInst *access) {
40084014
Address addr = getLoweredAddress(access->getSource());
@@ -4022,8 +4028,26 @@ void IRGenSILFunction::visitBeginUnpairedAccessInst(
40224028
Builder.CreateBitCast(addr.getAddress(), IGM.Int8PtrTy);
40234029
llvm::Value *flags =
40244030
llvm::ConstantInt::get(IGM.SizeTy, uint64_t(getExclusivityFlags(access)));
4031+
4032+
// Compute the effective PC of the access.
4033+
// Since begin_unpaired_access is designed for materializeForSet, our
4034+
// heuristic here is as well: we've either been inlined, in which case
4035+
// we should use the current PC (i.e. pass null), or we haven't,
4036+
// in which case we should use the caller, which is generally ok because
4037+
// materializeForSet can't usually be thunked.
4038+
llvm::Value *pc;
4039+
if (hasBeenInlined(access)) {
4040+
pc = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
4041+
} else {
4042+
auto retAddrFn =
4043+
llvm::Intrinsic::getDeclaration(IGM.getModule(),
4044+
llvm::Intrinsic::returnaddress);
4045+
pc = Builder.CreateCall(retAddrFn,
4046+
{ llvm::ConstantInt::get(IGM.Int32Ty, 0) });
4047+
}
4048+
40254049
auto call = Builder.CreateCall(IGM.getBeginAccessFn(),
4026-
{ pointer, scratch, flags });
4050+
{ pointer, scratch, flags, pc });
40274051
call->setDoesNotThrow();
40284052
return;
40294053
}

stdlib/public/runtime/Exclusivity.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@
4141
#include <stdio.h>
4242
#endif
4343

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

4658
bool swift::_swift_disableExclusivityChecking = false;
@@ -58,6 +70,7 @@ namespace {
5870
/// A single access that we're tracking.
5971
struct Access {
6072
void *Pointer;
73+
void *PC;
6174
uintptr_t NextAndAction;
6275

6376
enum : uintptr_t {
@@ -78,8 +91,10 @@ struct Access {
7891
return ExclusivityFlags(NextAndAction & ActionMask);
7992
}
8093

81-
void initialize(void *pointer, Access *next, ExclusivityFlags action) {
94+
void initialize(void *pc, void *pointer, Access *next,
95+
ExclusivityFlags action) {
8296
Pointer = pointer;
97+
PC = pc;
8398
NextAndAction = reinterpret_cast<uintptr_t>(next) | uintptr_t(action);
8499
}
85100
};
@@ -94,7 +109,7 @@ class AccessSet {
94109
public:
95110
constexpr AccessSet() {}
96111

97-
void insert(Access *access, void *pointer, ExclusivityFlags flags) {
112+
void insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags) {
98113
auto action = getAccessAction(flags);
99114

100115
for (Access *cur = Head; cur != nullptr; cur = cur->getNext()) {
@@ -110,13 +125,15 @@ class AccessSet {
110125
// Otherwise, it's a conflict.
111126
// TODO: try to recover source-location information from the return
112127
// address.
113-
fatalError(0, "%s/%s access conflict detected on address %p, aborting\n",
114-
getAccessName(action), getAccessName(cur->getAccessAction()),
128+
fatalError(0, "%s(%p) / %s(%p) access conflict detected on address %p,"
129+
" aborting\n",
130+
getAccessName(cur->getAccessAction()), cur->PC,
131+
getAccessName(action), pc,
115132
pointer);
116133
}
117134

118135
// Insert to the front of the array so that remove tends to find it faster.
119-
access->initialize(pointer, Head, action);
136+
access->initialize(pc, pointer, Head, action);
120137
Head = access;
121138
}
122139

@@ -214,7 +231,7 @@ static AccessSet &getAccessSet(void *pointer) {
214231
/// This may cause a runtime failure if an incompatible access is
215232
/// already underway.
216233
void swift::swift_beginAccess(void *pointer, ValueBuffer *buffer,
217-
ExclusivityFlags flags) {
234+
ExclusivityFlags flags, void *pc) {
218235
assert(pointer && "beginning an access on a null pointer?");
219236

220237
Access *access = reinterpret_cast<Access*>(buffer);
@@ -226,7 +243,9 @@ void swift::swift_beginAccess(void *pointer, ValueBuffer *buffer,
226243
return;
227244
}
228245

229-
getAccessSet(pointer).insert(access, pointer, flags);
246+
if (!pc) pc = get_return_address();
247+
248+
getAccessSet(pointer).insert(access, pc, pointer, flags);
230249
}
231250

232251
/// End tracking a dynamic access.

test/IRGen/access_markers.sil

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ bb0(%0 : $A):
2727
// CHECK-NEXT: [[T0:%.*]] = bitcast [[BUFFER]]* [[SCRATCH1]] to i8*
2828
// CHECK-NEXT: call void @llvm.lifetime.start(i64 {{.*}}, i8* [[T0]])
2929
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
30-
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH1]], [[SIZE]] 1)
30+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH1]], [[SIZE]] 1, i8* null)
3131
%3 = begin_access [modify] [dynamic] %2 : $*Int
3232

3333
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH1]])
@@ -38,7 +38,7 @@ bb0(%0 : $A):
3838
// CHECK-NEXT: [[T0:%.*]] = bitcast [[BUFFER]]* [[SCRATCH2]] to i8*
3939
// CHECK-NEXT: call void @llvm.lifetime.start(i64 {{.*}}, i8* [[T0]])
4040
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
41-
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH2]], [[SIZE]] 0)
41+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH2]], [[SIZE]] 0, i8* null)
4242
%5 = begin_access [read] [dynamic] %2 : $*Int
4343

4444
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH2]])
@@ -60,14 +60,14 @@ bb0(%0 : $A):
6060
%2 = ref_element_addr %0 : $A, #A.property
6161

6262
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
63-
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH]], [[SIZE]] 1)
63+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH]], [[SIZE]] 1, i8* null)
6464
%3 = begin_unpaired_access [modify] [dynamic] %2 : $*Int, %1 : $*Builtin.UnsafeValueBuffer
6565

6666
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH]])
6767
%4 = end_unpaired_access [dynamic] %1 : $*Builtin.UnsafeValueBuffer
6868

6969
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
70-
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH]], [[SIZE]] 0)
70+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH]], [[SIZE]] 0, i8* null)
7171
%5 = begin_unpaired_access [read] [dynamic] %2 : $*Int, %1 : $*Builtin.UnsafeValueBuffer
7272

7373
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH]])
@@ -79,6 +79,32 @@ bb0(%0 : $A):
7979
return %20 : $()
8080
}
8181

82+
// CHECK-LABEL: define {{.*}}void @testUnpairedExternal(
83+
sil @testUnpairedExternal : $(@guaranteed A, @inout Builtin.UnsafeValueBuffer) -> () {
84+
bb0(%0 : $A, %1 : $*Builtin.UnsafeValueBuffer):
85+
// CHECK: [[PROPERTY:%.*]] = getelementptr inbounds [[C]], [[C]]* %0, i32 0, i32 1
86+
%2 = ref_element_addr %0 : $A, #A.property
87+
88+
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
89+
// CHECK-NEXT: [[PC:%.*]] = call i8* @llvm.returnaddress(i32 0)
90+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH:%1]], [[SIZE]] 1, i8* [[PC]])
91+
%3 = begin_unpaired_access [modify] [dynamic] %2 : $*Int, %1 : $*Builtin.UnsafeValueBuffer
92+
93+
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH]])
94+
%4 = end_unpaired_access [dynamic] %1 : $*Builtin.UnsafeValueBuffer
95+
96+
// CHECK-NEXT: [[T1:%.*]] = bitcast [[INT]]* [[PROPERTY]] to i8*
97+
// CHECK-NEXT: [[PC:%.*]] = call i8* @llvm.returnaddress(i32 0)
98+
// CHECK-NEXT: call void @swift_beginAccess(i8* [[T1]], [[BUFFER]]* [[SCRATCH]], [[SIZE]] 0, i8* [[PC]])
99+
%5 = begin_unpaired_access [read] [dynamic] %2 : $*Int, %1 : $*Builtin.UnsafeValueBuffer
100+
101+
// CHECK-NEXT: call void @swift_endAccess([[BUFFER]]* [[SCRATCH]])
102+
%6 = end_unpaired_access [dynamic] %1 : $*Builtin.UnsafeValueBuffer
103+
104+
%20 = tuple ()
105+
return %20 : $()
106+
}
107+
82108
// rdar://31964550
83109
// Just check that this doesn't crash.
84110
sil @testCopyAddr : $(@guaranteed A) -> () {

test/IRGen/exclusivity.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ bb0(%0 : $A):
2424
// CHECK: [[T0:%.*]] = bitcast [[BUFFER_T]]* [[SCRATCH0]] to i8*
2525
// CHECK: call void @llvm.lifetime.start(i64 -1, i8* [[T0]])
2626
// CHECK: [[T0:%.*]] = bitcast [[INT:%TSi]]* [[PROP]] to i8*
27-
// CHECK: call void @swift_beginAccess(i8* [[T0]], [[BUFFER_T]]* [[SCRATCH0]], [[SIZE_T:i32|i64]] 0)
27+
// CHECK: call void @swift_beginAccess(i8* [[T0]], [[BUFFER_T]]* [[SCRATCH0]], [[SIZE_T:i32|i64]] 0, i8* null)
2828
%4 = begin_access [read] [dynamic] %3 : $*Int
2929

3030
// CHECK: [[T0:%.*]] = getelementptr inbounds [[INT]], [[INT]]* %1, i32 0, i32 0
@@ -44,7 +44,7 @@ bb0(%0 : $A):
4444
// CHECK: [[T0:%.*]] = bitcast [[BUFFER_T]]* [[SCRATCH1]] to i8*
4545
// CHECK: call void @llvm.lifetime.start(i64 -1, i8* [[T0]])
4646
// CHECK: [[T0:%.*]] = bitcast [[INT:%TSi]]* [[PROP]] to i8*
47-
// CHECK: call void @swift_beginAccess(i8* [[T0]], [[BUFFER_T]]* [[SCRATCH1]], [[SIZE_T]] 1)
47+
// CHECK: call void @swift_beginAccess(i8* [[T0]], [[BUFFER_T]]* [[SCRATCH1]], [[SIZE_T]] 1, i8* null)
4848
%12 = begin_access [modify] [dynamic] %11 : $*Int
4949

5050
// CHECK: call {{.*}} @changeInt([[INT]]*{{.*}} [[PROP]])

0 commit comments

Comments
 (0)