25
25
26
26
#define DEBUG_TYPE " static-exclusivity"
27
27
#include " swift/AST/DiagnosticsSIL.h"
28
+ #include " swift/AST/ASTWalker.h"
29
+ #include " swift/AST/Decl.h"
30
+ #include " swift/AST/Expr.h"
31
+ #include " swift/AST/Stmt.h"
28
32
#include " swift/SIL/CFG.h"
29
33
#include " swift/SIL/SILInstruction.h"
30
34
#include " swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
@@ -163,6 +167,12 @@ enum class ExclusiveOrShared_t : unsigned {
163
167
// / Tracks the in-progress accesses on per-storage-location basis.
164
168
using StorageMap = llvm::SmallDenseMap<AccessedStorage, AccessInfo, 4 >;
165
169
170
+ // / A pair of 'begin_access' instructions that conflict.
171
+ struct ConflictingAccess {
172
+ const BeginAccessInst *FirstAccess;
173
+ const BeginAccessInst *SecondAccess;
174
+ };
175
+
166
176
} // end anonymous namespace
167
177
168
178
namespace llvm {
@@ -214,6 +224,58 @@ static ExclusiveOrShared_t getRequiredAccess(const BeginAccessInst *BAI) {
214
224
return ExclusiveOrShared_t::ExclusiveAccess;
215
225
}
216
226
227
+ // / Perform a syntactic suppression that returns true when the accesses are for
228
+ // / inout arguments to a call that corresponds to to one of the passed-in
229
+ // / 'apply' instructions.
230
+ static bool
231
+ isConflictOnInoutArgumentsToSuppressed (const BeginAccessInst *Access1,
232
+ const BeginAccessInst *Access2,
233
+ ArrayRef<ApplyInst *> CallsToSuppress) {
234
+ if (CallsToSuppress.empty ())
235
+ return false ;
236
+
237
+ // Inout arguments must be modifications.
238
+ if (Access1->getAccessKind () != SILAccessKind::Modify ||
239
+ Access2->getAccessKind () != SILAccessKind::Modify) {
240
+ return false ;
241
+ }
242
+
243
+ SILLocation Loc1 = Access1->getLoc ();
244
+ SILLocation Loc2 = Access2->getLoc ();
245
+ if (Loc1.isNull () || Loc2.isNull ())
246
+ return false ;
247
+
248
+ auto *InOut1 = Loc1.getAsASTNode <InOutExpr>();
249
+ auto *InOut2 = Loc2.getAsASTNode <InOutExpr>();
250
+ if (!InOut1 || !InOut2)
251
+ return false ;
252
+
253
+ for (ApplyInst *AI : CallsToSuppress) {
254
+ SILLocation CallLoc = AI->getLoc ();
255
+ if (CallLoc.isNull ())
256
+ continue ;
257
+
258
+ auto *CE = CallLoc.getAsASTNode <CallExpr>();
259
+ if (!CE)
260
+ continue ;
261
+
262
+ bool FoundTarget = false ;
263
+ CE->forEachChildExpr ([=,&FoundTarget](Expr *Child) {
264
+ if (Child == InOut1) {
265
+ FoundTarget = true ;
266
+ // Stops the traversal.
267
+ return (Expr *)nullptr ;
268
+ }
269
+ return Child;
270
+ }
271
+ );
272
+ if (FoundTarget)
273
+ return true ;
274
+ }
275
+
276
+ return false ;
277
+ }
278
+
217
279
// / Emits a diagnostic if beginning an access with the given in-progress
218
280
// / accesses violates the law of exclusivity. Returns true when a
219
281
// / diagnostic was emitted.
@@ -256,6 +318,23 @@ static AccessedStorage findAccessedStorage(SILValue Source) {
256
318
}
257
319
}
258
320
321
+ // / Returns true when the apply calls the Standard Library swap().
322
+ // / Used for diagnostic suppression.
323
+ bool isCallToStandardLibrarySwap (ApplyInst *AI, ASTContext &Ctx) {
324
+ SILFunction *SF = AI->getReferencedFunction ();
325
+ if (!SF)
326
+ return false ;
327
+
328
+ if (!SF->hasLocation ())
329
+ return false ;
330
+
331
+ auto *FD = SF->getLocation ().getAsASTNode <FuncDecl>();
332
+ if (!FD)
333
+ return false ;
334
+
335
+ return FD == Ctx.getSwap (nullptr );
336
+ }
337
+
259
338
static void checkStaticExclusivity (SILFunction &Fn, PostOrderFunctionInfo *PO) {
260
339
// The implementation relies on the following SIL invariants:
261
340
// - All incoming edges to a block must have the same in-progress
@@ -275,6 +354,15 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
275
354
if (Fn.empty ())
276
355
return ;
277
356
357
+ // Collects calls to functions for which diagnostics about conflicting inout
358
+ // arguments should be suppressed.
359
+ llvm::SmallVector<ApplyInst *, 8 > CallsToSuppress;
360
+
361
+ // Stores the accesses that have been found to conflict. Used to defer
362
+ // emitting diagnostics until we can determine whether they should
363
+ // be suppressed.
364
+ llvm::SmallVector<ConflictingAccess, 4 > ConflictingAccesses;
365
+
278
366
// For each basic block, track the stack of current accesses on
279
367
// exit from that block.
280
368
llvm::SmallDenseMap<SILBasicBlock *, Optional<StorageMap>, 32 >
@@ -316,7 +404,7 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
316
404
if (Info.conflictsWithAccess (Kind) && !Info.alreadyHadConflict ()) {
317
405
const BeginAccessInst *Conflict = Info.getFirstAccess ();
318
406
assert (Conflict && " Must already have had access to conflict!" );
319
- diagnoseExclusivityViolation ( Conflict, BAI, Fn. getASTContext () );
407
+ ConflictingAccesses. push_back ({ Conflict, BAI } );
320
408
}
321
409
322
410
Info.beginAccess (BAI);
@@ -335,12 +423,31 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
335
423
continue ;
336
424
}
337
425
426
+ if (auto *AI = dyn_cast<ApplyInst>(&I)) {
427
+ // Suppress for the arguments to the Standard Library's swap()
428
+ // function until we can recommend a safe alternative.
429
+ if (isCallToStandardLibrarySwap (AI, Fn.getASTContext ()))
430
+ CallsToSuppress.push_back (AI);
431
+ }
432
+
338
433
if (isa<ReturnInst>(&I)) {
339
434
// Sanity check to make sure entries are properly removed.
340
435
assert (Accesses.size () == 0 );
341
436
}
342
437
}
343
438
}
439
+
440
+ // Now that we've collected violations and suppressed calls, emit
441
+ // diagnostics.
442
+ for (auto &Violation : ConflictingAccesses) {
443
+ const BeginAccessInst *PriorAccess = Violation.FirstAccess ;
444
+ const BeginAccessInst *NewAccess = Violation.SecondAccess ;
445
+ if (isConflictOnInoutArgumentsToSuppressed (PriorAccess, NewAccess,
446
+ CallsToSuppress))
447
+ continue ;
448
+
449
+ diagnoseExclusivityViolation (PriorAccess, NewAccess, Fn.getASTContext ());
450
+ }
344
451
}
345
452
346
453
namespace {
0 commit comments