//===--- 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/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 only dynamic checks when dynamic // checking is enabled. Otherwise, this pass removes all checks. // // This is currently unsupported because tail duplication results in // address-type block arguments. llvm::cl::opt 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) {} SILBasicBlock::iterator eraseInst(SILInstruction *inst) { DEBUG(llvm::dbgs() << "Erasing access marker: " << *inst); removedAny = true; return inst->getParent()->erase(inst); }; void replaceBeginAccessUsers(BeginAccessInst *beginAccess); 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. bool checkAndEliminateMarker(SILInstruction *inst); // Entry point called either by the pass by the same name // or as a utility (e.g. during deserialization). bool stripMarkers(); }; void AccessMarkerElimination::replaceBeginAccessUsers( BeginAccessInst *beginAccess) { // Handle all the uses: while (!beginAccess->use_empty()) { Operand *op = *beginAccess->use_begin(); // Delete any associated end_access instructions. if (auto endAccess = dyn_cast(op->getUser())) { endAccess->eraseFromParent(); // Forward all other uses to the original address. } else { op->set(beginAccess->getSource()); } } } bool AccessMarkerElimination::shouldPreserveAccess( SILAccessEnforcement enforcement) { if (!EnableOptimizedAccessMarkers) return false; switch (enforcement) { case SILAccessEnforcement::Static: case SILAccessEnforcement::Unsafe: return false; case SILAccessEnforcement::Unknown: case SILAccessEnforcement::Dynamic: return Mod->getOptions().EnforceExclusivityDynamic; } } // 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. bool AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { if (auto beginAccess = dyn_cast(inst)) { // Leave dynamic accesses in place, but delete all others. if (shouldPreserveAccess(beginAccess->getEnforcement())) return false; replaceBeginAccessUsers(beginAccess); return true; } // 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(inst)) { if (shouldPreserveAccess(BUA->getEnforcement())) return false; return true; } // end_unpaired_access instructions will be directly removed and // simply replaced with their operand. if (auto EUA = dyn_cast(inst)) { if (shouldPreserveAccess(EUA->getEnforcement())) return false; return true; } return false; } // 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); if (checkAndEliminateMarker(inst)) II = eraseInst(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) { 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) M.registerDeserializationCallback(prepareSILFunctionForOptimization); } } }; } // end anonymous namespace SILTransform *swift::createAccessMarkerElimination() { return new AccessMarkerEliminationPass(); }