blob: d0811fd244dba678bbded000e0d3464079044eed [file] [log] [blame]
//===--- Context.cpp ------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-semantic-arc-opts"
#include "Context.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/Projection.h"
#include "llvm/Support/Debug.h"
using namespace swift;
using namespace swift::semanticarc;
static llvm::cl::opt<bool>
VerifyAfterTransformOption("sil-semantic-arc-opts-verify-after-transform",
llvm::cl::Hidden, llvm::cl::init(false));
void Context::verify() const {
if (VerifyAfterTransformOption)
fn.verify();
}
//===----------------------------------------------------------------------===//
// Well Behaved Write Analysis
//===----------------------------------------------------------------------===//
/// Returns true if we were able to ascertain that either the initialValue has
/// no write uses or all of the write uses were writes that we could understand.
bool Context::constructCacheValue(
SILValue initialValue,
SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator) {
SmallVector<Operand *, 8> worklist(initialValue->getUses());
while (!worklist.empty()) {
auto *op = worklist.pop_back_val();
SILInstruction *user = op->getUser();
if (Projection::isAddressProjection(user) ||
isa<ProjectBlockStorageInst>(user)) {
for (SILValue r : user->getResults()) {
llvm::copy(r->getUses(), std::back_inserter(worklist));
}
continue;
}
if (auto *oeai = dyn_cast<OpenExistentialAddrInst>(user)) {
// Mutable access!
if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) {
wellBehavedWriteAccumulator.push_back(op);
}
// Otherwise, look through it and continue.
llvm::copy(oeai->getUses(), std::back_inserter(worklist));
continue;
}
if (auto *si = dyn_cast<StoreInst>(user)) {
// We must be the dest since addresses can not be stored.
assert(si->getDest() == op->get());
wellBehavedWriteAccumulator.push_back(op);
continue;
}
// Add any destroy_addrs to the resultAccumulator.
if (isa<DestroyAddrInst>(user)) {
wellBehavedWriteAccumulator.push_back(op);
continue;
}
// load_borrow and incidental uses are fine as well.
if (isa<LoadBorrowInst>(user) || isIncidentalUse(user)) {
continue;
}
// Look through begin_access and mark them/their end_borrow as users.
if (auto *bai = dyn_cast<BeginAccessInst>(user)) {
// If we do not have a read, mark this as a write. Also, insert our
// end_access as well.
if (bai->getAccessKind() != SILAccessKind::Read) {
wellBehavedWriteAccumulator.push_back(op);
transform(bai->getUsersOfType<EndAccessInst>(),
std::back_inserter(wellBehavedWriteAccumulator),
[](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
}
// And then add the users to the worklist and continue.
llvm::copy(bai->getUses(), std::back_inserter(worklist));
continue;
}
// If we have a load, we just need to mark the load [take] as a write.
if (auto *li = dyn_cast<LoadInst>(user)) {
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
wellBehavedWriteAccumulator.push_back(op);
}
continue;
}
// If we have a FullApplySite, we need to do per convention/inst logic.
if (auto fas = FullApplySite::isa(user)) {
// Begin by seeing if we have an in_guaranteed use. If we do, we are done.
if (fas.getArgumentConvention(*op) ==
SILArgumentConvention::Indirect_In_Guaranteed) {
continue;
}
// Then see if we have an apply site that is not a coroutine apply
// site. In such a case, without further analysis, we can treat it like an
// instantaneous write and validate that it doesn't overlap with our load
// [copy].
if (!fas.beginsCoroutineEvaluation() &&
fas.getArgumentConvention(*op).isInoutConvention()) {
wellBehavedWriteAccumulator.push_back(op);
continue;
}
// Otherwise, be conservative and return that we had a write that we did
// not understand.
LLVM_DEBUG(llvm::dbgs()
<< "Function: " << user->getFunction()->getName() << "\n");
LLVM_DEBUG(llvm::dbgs() << "Value: " << op->get());
LLVM_DEBUG(llvm::dbgs() << "Unhandled apply site!: " << *user);
return false;
}
// Copy addr that read are just loads.
if (auto *cai = dyn_cast<CopyAddrInst>(user)) {
// If our value is the destination, this is a write.
if (cai->getDest() == op->get()) {
wellBehavedWriteAccumulator.push_back(op);
continue;
}
// Ok, so we are Src by process of elimination. Make sure we are not being
// taken.
if (cai->isTakeOfSrc()) {
wellBehavedWriteAccumulator.push_back(op);
continue;
}
// Otherwise, we are safe and can continue.
continue;
}
// If we did not recognize the user, just return conservatively that it was
// written to in a way we did not understand.
LLVM_DEBUG(llvm::dbgs()
<< "Function: " << user->getFunction()->getName() << "\n");
LLVM_DEBUG(llvm::dbgs() << "Value: " << op->get());
LLVM_DEBUG(llvm::dbgs() << "Unknown instruction!: " << *user);
return false;
}
// Ok, we finished our worklist and this address is not being written to.
return true;
}