static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, AccessSummaryAnalysis *ASA) { // The implementation relies on the following SIL invariants: // - All incoming edges to a block must have the same in-progress // accesses. This enables the analysis to not perform a data flow merge // on incoming edges. // - Further, for a given address each of the in-progress // accesses must have begun in the same order on all edges. This ensures // consistent diagnostics across changes to the exploration of the CFG. // - On return from a function there are no in-progress accesses. This // enables a sanity check for lean analysis state at function exit. // - Each end_access instruction corresponds to exactly one begin access // instruction. (This is encoded in the EndAccessInst itself) // - begin_access arguments cannot be basic block arguments. // This enables the analysis to look back to find the *single* storage // storage location accessed. if (Fn.empty()) return; AccessState State(ASA); // For each basic block, track the stack of current accesses on // exit from that block. llvm::SmallDenseMap<SILBasicBlock *, Optional<StorageMap>, 32> BlockOutAccesses; BlockOutAccesses[Fn.getEntryBlock()] = StorageMap(); for (auto *BB : PO->getReversePostOrder()) { Optional<StorageMap> &BBState = BlockOutAccesses[BB]; // Because we use a reverse post-order traversal, unless this is the entry // at least one of its predecessors must have been reached. Use the out // state for that predecessor as our in state. The SIL verifier guarantees // that all incoming edges must have the same current accesses. for (auto *Pred : BB->getPredecessorBlocks()) { auto it = BlockOutAccesses.find(Pred); if (it == BlockOutAccesses.end()) continue; const Optional<StorageMap> &PredAccesses = it->getSecond(); if (PredAccesses) { BBState = PredAccesses; break; } } // The in-progress accesses for the current program point, represented // as map from storage locations to the accesses in progress for the // location. State.Accesses = BBState.getPointer(); for (auto &I : *BB) checkForViolationsAtInstruction(I, State); } // Now that we've collected violations and suppressed calls, emit // diagnostics. for (auto &Violation : State.ConflictingAccesses) { diagnoseExclusivityViolation(Violation, State.CallsToSwap, Fn.getASTContext()); } }
static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO, AccessSummaryAnalysis *ASA) { // The implementation relies on the following SIL invariants: // - All incoming edges to a block must have the same in-progress // accesses. This enables the analysis to not perform a data flow merge // on incoming edges. // - Further, for a given address each of the in-progress // accesses must have begun in the same order on all edges. This ensures // consistent diagnostics across changes to the exploration of the CFG. // - On return from a function there are no in-progress accesses. This // enables a sanity check for lean analysis state at function exit. // - Each end_access instruction corresponds to exactly one begin access // instruction. (This is encoded in the EndAccessInst itself) // - begin_access arguments cannot be basic block arguments. // This enables the analysis to look back to find the *single* storage // storage location accessed. if (Fn.empty()) return; // Collects calls the Standard Library swap() for Fix-Its. llvm::SmallVector<ApplyInst *, 8> CallsToSwap; // Stores the accesses that have been found to conflict. Used to defer // emitting diagnostics until we can determine whether they should // be suppressed. llvm::SmallVector<ConflictingAccess, 4> ConflictingAccesses; // For each basic block, track the stack of current accesses on // exit from that block. llvm::SmallDenseMap<SILBasicBlock *, Optional<StorageMap>, 32> BlockOutAccesses; BlockOutAccesses[Fn.getEntryBlock()] = StorageMap(); for (auto *BB : PO->getReversePostOrder()) { Optional<StorageMap> &BBState = BlockOutAccesses[BB]; // Because we use a reverse post-order traversal, unless this is the entry // at least one of its predecessors must have been reached. Use the out // state for that predecessor as our in state. The SIL verifier guarantees // that all incoming edges must have the same current accesses. for (auto *Pred : BB->getPredecessorBlocks()) { auto it = BlockOutAccesses.find(Pred); if (it == BlockOutAccesses.end()) continue; const Optional<StorageMap> &PredAccesses = it->getSecond(); if (PredAccesses) { BBState = PredAccesses; break; } } // The in-progress accesses for the current program point, represented // as map from storage locations to the accesses in progress for the // location. StorageMap &Accesses = *BBState; for (auto &I : *BB) { // Apply transfer functions. Beginning an access // increments the read or write count for the storage location; // Ending onr decrements the count. if (auto *BAI = dyn_cast<BeginAccessInst>(&I)) { SILAccessKind Kind = BAI->getAccessKind(); const AccessedStorage &Storage = findAccessedStorage(BAI->getSource()); AccessInfo &Info = Accesses[Storage]; const IndexTrieNode *SubPath = ASA->findSubPathAccessed(BAI); if (auto Conflict = shouldReportAccess(Info, Kind, SubPath)) { ConflictingAccesses.emplace_back(Storage, *Conflict, RecordedAccess(BAI, SubPath)); } Info.beginAccess(BAI, SubPath); continue; } if (auto *EAI = dyn_cast<EndAccessInst>(&I)) { auto It = Accesses.find(findAccessedStorage(EAI->getSource())); AccessInfo &Info = It->getSecond(); BeginAccessInst *BAI = EAI->getBeginAccess(); const IndexTrieNode *SubPath = ASA->findSubPathAccessed(BAI); Info.endAccess(EAI, SubPath); // If the storage location has no more in-progress accesses, remove // it to keep the StorageMap lean. if (!Info.hasAccessesInProgress()) Accesses.erase(It); continue; } if (auto *AI = dyn_cast<ApplyInst>(&I)) { // Record calls to swap() for potential Fix-Its. if (isCallToStandardLibrarySwap(AI, Fn.getASTContext())) CallsToSwap.push_back(AI); else checkForViolationsInNoEscapeClosures(Accesses, AI, ASA, ConflictingAccesses); continue; } if (auto *TAI = dyn_cast<TryApplyInst>(&I)) { checkForViolationsInNoEscapeClosures(Accesses, TAI, ASA, ConflictingAccesses); continue; } // Sanity check to make sure entries are properly removed. assert((!isa<ReturnInst>(&I) || Accesses.size() == 0) && "Entries were not properly removed?!"); } } // Now that we've collected violations and suppressed calls, emit // diagnostics. for (auto &Violation : ConflictingAccesses) { diagnoseExclusivityViolation(Violation, CallsToSwap, Fn.getASTContext()); } }