| //===--- AccessMarkerElimination.cpp - Eliminate access markers. ----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// This pass eliminates the instructions that demarcate memory access regions. |
| /// If no memory access markers exist, then the pass does nothing. Otherwise, it |
| /// unconditionally eliminates all non-dynamic markers (plus any dynamic markers |
| /// if dynamic exclusivity checking is disabled). |
| /// |
| /// This is an always-on pass for temporary bootstrapping. It allows running |
| /// test cases through the pipeline and exercising SIL verification before all |
| /// passes support access markers. |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "access-marker-elim" |
| #include "swift/Basic/Range.h" |
| #include "swift/SIL/MemAccessUtils.h" |
| #include "swift/SIL/SILFunction.h" |
| #include "swift/SILOptimizer/PassManager/Transforms.h" |
| #include "llvm/Support/CommandLine.h" |
| |
| using namespace swift; |
| |
| // This temporary option allows markers during optimization passes. Enabling |
| // this flag causes this pass to preserve all access markers. Otherwise, it only |
| // preserved "dynamic" markers. |
| llvm::cl::opt<bool> EnableOptimizedAccessMarkers( |
| "sil-optimized-access-markers", llvm::cl::init(false), |
| llvm::cl::desc("Enable memory access markers during optimization passes.")); |
| |
| namespace { |
| |
| struct AccessMarkerElimination { |
| SILModule *Mod; |
| SILFunction *F; |
| |
| bool removedAny = false; |
| |
| AccessMarkerElimination(SILFunction *F) |
| : Mod(&F->getModule()), F(F) {} |
| |
| void notifyErased(SILInstruction *inst) { |
| LLVM_DEBUG(llvm::dbgs() << "Erasing access marker: " << *inst); |
| removedAny = true; |
| } |
| |
| SILBasicBlock::iterator eraseInst(SILInstruction *inst) { |
| notifyErased(inst); |
| return inst->getParent()->erase(inst); |
| }; |
| |
| bool shouldPreserveAccess(SILAccessEnforcement enforcement); |
| |
| // Check if the instruction is a marker that should be eliminated. If so, |
| // updated the SIL, short of erasing the marker itself, and return true. |
| SILBasicBlock::iterator checkAndEliminateMarker(SILInstruction *inst); |
| |
| // Entry point called either by the pass by the same name |
| // or as a utility (e.g. during deserialization). |
| bool stripMarkers(); |
| }; |
| |
| bool AccessMarkerElimination::shouldPreserveAccess( |
| SILAccessEnforcement enforcement) { |
| if (EnableOptimizedAccessMarkers || Mod->getOptions().VerifyExclusivity) |
| return true; |
| |
| switch (enforcement) { |
| case SILAccessEnforcement::Static: |
| case SILAccessEnforcement::Unsafe: |
| return false; |
| case SILAccessEnforcement::Unknown: |
| case SILAccessEnforcement::Dynamic: |
| return Mod->getOptions().EnforceExclusivityDynamic; |
| } |
| llvm_unreachable("unhandled enforcement"); |
| } |
| |
| // Check if the instruction is a marker that should be eliminated. If so, delete |
| // the begin_access along with all associated end_access and a valid instruction |
| // iterator pointing to the first remaining instruction following the |
| // begin_access. If the marker is not eliminated, return an iterator pointing to |
| // the marker. |
| SILBasicBlock::iterator |
| AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { |
| if (auto beginAccess = dyn_cast<BeginAccessInst>(inst)) { |
| // Builtins used by the standard library must emit markers regardless of the |
| // current compiler options so that any user code that initiates access via |
| // the standard library is fully enforced. |
| if (beginAccess->isFromBuiltin()) |
| return inst->getIterator(); |
| |
| // Leave dynamic accesses in place, but delete all others. |
| if (shouldPreserveAccess(beginAccess->getEnforcement())) |
| return inst->getIterator(); |
| |
| notifyErased(beginAccess); |
| return removeBeginAccess(beginAccess); |
| } |
| |
| // end_access instructions will be handled when we process the |
| // begin_access. |
| |
| // begin_unpaired_access instructions will be directly removed and |
| // simply replaced with their operand. |
| if (auto BUA = dyn_cast<BeginUnpairedAccessInst>(inst)) { |
| // Builtins used by the standard library must emit markers regardless of the |
| // current compiler options. |
| if (BUA->isFromBuiltin()) |
| return inst->getIterator(); |
| |
| if (shouldPreserveAccess(BUA->getEnforcement())) |
| return inst->getIterator(); |
| |
| return eraseInst(BUA); |
| } |
| // end_unpaired_access instructions will be directly removed and |
| // simply replaced with their operand. |
| if (auto EUA = dyn_cast<EndUnpairedAccessInst>(inst)) { |
| // Builtins used by the standard library must emit markers regardless of the |
| // current compiler options. |
| if (EUA->isFromBuiltin()) |
| return inst->getIterator(); |
| |
| if (shouldPreserveAccess(EUA->getEnforcement())) |
| return inst->getIterator(); |
| |
| return eraseInst(EUA); |
| } |
| return inst->getIterator(); |
| } |
| |
| // Top-level per-function entry-point. |
| // Return `true` if any markers were removed. |
| bool AccessMarkerElimination::stripMarkers() { |
| // Iterating in reverse eliminates more begin_access users before they |
| // need to be replaced. |
| for (auto &BB : reversed(*F)) { |
| // Don't cache the begin iterator since we're reverse iterating. |
| for (auto II = BB.end(); II != BB.begin();) { |
| SILInstruction *inst = &*(--II); |
| // checkAndEliminateMarker returns the next non-deleted instruction. The |
| // following iteration moves the iterator backward. |
| II = checkAndEliminateMarker(inst); |
| } |
| } |
| return removedAny; |
| } |
| |
| } // end anonymous namespace |
| |
| // Implement a SILModule::SILFunctionBodyCallback that strips all access |
| // markers from newly deserialized function bodies. |
| static void prepareSILFunctionForOptimization(ModuleDecl *, SILFunction *F) { |
| LLVM_DEBUG(llvm::dbgs() << "Stripping all markers in: " << F->getName() |
| << "\n"); |
| |
| AccessMarkerElimination(F).stripMarkers(); |
| } |
| |
| namespace { |
| |
| struct AccessMarkerEliminationPass : SILModuleTransform { |
| void run() override { |
| auto &M = *getModule(); |
| for (auto &F : M) { |
| bool removedAny = AccessMarkerElimination(&F).stripMarkers(); |
| |
| // Only invalidate analyses if we removed some markers. |
| if (removedAny) { |
| auto InvalidKind = SILAnalysis::InvalidationKind::Instructions; |
| invalidateAnalysis(&F, InvalidKind); |
| } |
| |
| // Markers from all current SIL functions are stripped. Register a |
| // callback to strip an subsequently loaded functions on-the-fly. |
| if (!EnableOptimizedAccessMarkers) { |
| using NotificationHandlerTy = |
| FunctionBodyDeserializationNotificationHandler; |
| auto *n = new NotificationHandlerTy(prepareSILFunctionForOptimization); |
| std::unique_ptr<DeserializationNotificationHandler> ptr(n); |
| M.registerDeserializationNotificationHandler(std::move(ptr)); |
| } |
| } |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| SILTransform *swift::createAccessMarkerElimination() { |
| return new AccessMarkerEliminationPass(); |
| } |