mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
For now I am trying out /not/ expanding when ownership is enabled. I still fixed the problems in it though. I also put in a force expand everything pass to make sure that in ossa we can still successfully go down this code path if we want to. The reason why I think this is the right thing to do is that the original reason why lower aggregate instrs was written was to help the low level arc optimizer (which only runs on non-ossa code). To the high level arc optimizer this is just noise and will keep the IR simpler. Another thing to note is that I updated the code so it should work in both ossa and non-ossa. In order to not perturb the code too much, I had to add a small optimization to TypeLowering where when ownership is disabled, we do not reform aggregates when copying. Instead, we just return the passed in value. This makes the pass the same when optimizing in both modes.
331 lines
11 KiB
C++
331 lines
11 KiB
C++
//===--- SILLowerAggregateInstrs.cpp - Aggregate insts to Scalar insts ---===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
///
|
|
/// Simplify aggregate instructions into scalar instructions using simple
|
|
/// peephole transformations.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-lower-aggregate-instrs"
|
|
#include "swift/SIL/Projection.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
#include "swift/SIL/TypeLowering.h"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::Lowering;
|
|
|
|
STATISTIC(NumExpand, "Number of instructions expanded");
|
|
|
|
static llvm::cl::opt<bool> EnableExpandAll("sil-lower-agg-instrs-expand-all",
|
|
llvm::cl::init(false));
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// We only expand if we are not in ownership and shouldExpand is true. The
|
|
/// reason why is that this was originally done to help the low level ARC
|
|
/// optimizer. To the high level ARC optimizer, this is just noise and
|
|
/// unnecessary IR. At the same time for testing purposes, we want to provide a
|
|
/// way even with ownership enabled to expand so we can check correctness.
|
|
static bool shouldExpandShim(SILFunction *fn, SILType type) {
|
|
return EnableExpandAll ||
|
|
(!fn->hasOwnership() && shouldExpand(fn->getModule(), type));
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Higher Level Operation Expansion
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Lower copy_addr into loads/stores/retain/release if we have a
|
|
/// non-address only type. We do this here so we can process the resulting
|
|
/// loads/stores.
|
|
///
|
|
/// This peephole implements the following optimizations with the ossa version
|
|
/// of the optimization first.
|
|
///
|
|
/// copy_addr %0 to %1 : $*T
|
|
/// ->
|
|
/// %new = load [copy] %0 : $*T
|
|
/// store %new to [assign] %1 : $*T
|
|
/// ->
|
|
/// %new = load %0 : $*T // Load the new value from the source
|
|
/// strong_retain %new : $T // Retain the new value
|
|
/// %old = load %1 : $*T // Load the old value from the destination
|
|
/// strong_release %old : $T // Release the old
|
|
/// store %new to %1 : $*T // Store the new value to the destination
|
|
///
|
|
/// copy_addr [take] %0 to %1 : $*T
|
|
/// ->
|
|
/// // load [take], not load [copy]!
|
|
/// %new = load [take] %0 : $*T
|
|
/// store %new to [assign] %1 : $*T
|
|
/// ->
|
|
/// %new = load %0 : $*T
|
|
/// // no retain of %new!
|
|
/// %old = load %1 : $*T
|
|
/// strong_release %old : $T
|
|
/// store %new to %1 : $*T
|
|
///
|
|
/// copy_addr %0 to [initialization] %1 : $*T
|
|
/// ->
|
|
/// %new = load [copy] %0 : $*T
|
|
/// store %new to [init] %1 : $*T
|
|
/// ->
|
|
/// %new = load %0 : $*T
|
|
/// strong_retain %new : $T
|
|
/// // no load/release of %old!
|
|
/// store %new to %1 : $*T
|
|
///
|
|
/// copy_addr [take] %0 to [initialization] %1 : $*T
|
|
/// ->
|
|
/// %new = load [take] %0 : $*T
|
|
/// store %new to [init] %1 : $*T
|
|
/// ->
|
|
/// %new = load %0 : $*T
|
|
/// // no retain of %new!
|
|
/// // no load/release of %old!
|
|
/// store %new to %1 : $*T
|
|
static bool expandCopyAddr(CopyAddrInst *cai) {
|
|
SILFunction *fn = cai->getFunction();
|
|
SILValue source = cai->getSrc();
|
|
|
|
// If we have an address only type don't do anything.
|
|
SILType srcType = source->getType();
|
|
if (srcType.isAddressOnly(*fn))
|
|
return false;
|
|
|
|
bool expand = shouldExpandShim(fn, srcType.getObjectType());
|
|
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
|
|
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
|
|
: TypeExpansionKind::None;
|
|
|
|
SILBuilderWithScope builder(cai);
|
|
|
|
// If our object type is not trivial, we may need to destroy the old value and
|
|
// copy the new one. Handle the trivial case quickly and return.
|
|
if (srcType.isTrivial(*fn)) {
|
|
SILValue newValue = builder.emitLoadValueOperation(
|
|
cai->getLoc(), source, LoadOwnershipQualifier::Trivial);
|
|
SILValue destAddr = cai->getDest();
|
|
// Create the store.
|
|
builder.emitStoreValueOperation(cai->getLoc(), newValue, destAddr,
|
|
StoreOwnershipQualifier::Trivial);
|
|
++NumExpand;
|
|
return true;
|
|
}
|
|
|
|
// %new = load [copy|take] %0 : $*T
|
|
auto loadQual = [&]() -> LoadOwnershipQualifier {
|
|
if (IsTake_t::IsTake == cai->isTakeOfSrc())
|
|
return LoadOwnershipQualifier::Take;
|
|
return LoadOwnershipQualifier::Copy;
|
|
}();
|
|
SILValue newValue = builder.emitLoweredLoadValueOperation(
|
|
cai->getLoc(), source, loadQual, expansionKind);
|
|
SILValue destAddr = cai->getDest();
|
|
|
|
// Create the store in the guaranteed uninitialized memory.
|
|
//
|
|
// store %new to [init|assign] %1
|
|
//
|
|
// If we are not initializing the destination, we need to destroy what is
|
|
// currently there before we re-initialize the memory.
|
|
auto storeQualifier = [&]() -> StoreOwnershipQualifier {
|
|
if (IsInitialization_t::IsInitialization != cai->isInitializationOfDest())
|
|
return StoreOwnershipQualifier::Assign;
|
|
return StoreOwnershipQualifier::Init;
|
|
}();
|
|
builder.emitLoweredStoreValueOperation(cai->getLoc(), newValue, destAddr,
|
|
storeQualifier, expansionKind);
|
|
|
|
++NumExpand;
|
|
return true;
|
|
}
|
|
|
|
static bool expandDestroyAddr(DestroyAddrInst *dai) {
|
|
SILFunction *fn = dai->getFunction();
|
|
SILBuilderWithScope builder(dai);
|
|
|
|
// Strength reduce destroy_addr inst into release/store if
|
|
// we have a non-address only type.
|
|
SILValue addr = dai->getOperand();
|
|
|
|
// If we have an address only type, do nothing.
|
|
SILType type = addr->getType();
|
|
if (type.isAddressOnly(*fn))
|
|
return false;
|
|
|
|
// We only expand if ownership is not enabled and we do not have a large
|
|
// type. This was something that was only really beneficial for the low level
|
|
// ARC optimizer which runs without ownership enabled.
|
|
bool expand = shouldExpandShim(fn, type.getObjectType());
|
|
|
|
// If we have a non-trivial type...
|
|
if (!type.isTrivial(*fn)) {
|
|
// If we have a type with reference semantics, emit a load/destroy.
|
|
SILValue li = builder.emitLoadValueOperation(dai->getLoc(), addr,
|
|
LoadOwnershipQualifier::Take);
|
|
auto &typeLowering = fn->getTypeLowering(type);
|
|
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
|
|
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
|
|
: TypeExpansionKind::None;
|
|
typeLowering.emitLoweredDestroyValue(builder, dai->getLoc(), li,
|
|
expansionKind);
|
|
}
|
|
|
|
++NumExpand;
|
|
return true;
|
|
}
|
|
|
|
static bool expandReleaseValue(ReleaseValueInst *rvi) {
|
|
SILFunction *fn = rvi->getFunction();
|
|
SILBuilderWithScope builder(rvi);
|
|
|
|
// Strength reduce destroy_addr inst into release/store if
|
|
// we have a non-address only type.
|
|
SILValue value = rvi->getOperand();
|
|
|
|
// If we have an address only type, do nothing.
|
|
SILType type = value->getType();
|
|
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
|
|
type.isLoadable(*fn) &&
|
|
"release_value should never be called on a non-loadable type.");
|
|
|
|
if (!shouldExpandShim(fn, type.getObjectType()))
|
|
return false;
|
|
|
|
auto &TL = fn->getTypeLowering(type);
|
|
TL.emitLoweredDestroyValueMostDerivedDescendents(builder, rvi->getLoc(),
|
|
value);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " Expanding: " << *rvi);
|
|
|
|
++NumExpand;
|
|
return true;
|
|
}
|
|
|
|
static bool expandRetainValue(RetainValueInst *rvi) {
|
|
SILFunction *fn = rvi->getFunction();
|
|
SILBuilderWithScope builder(rvi);
|
|
|
|
// Strength reduce destroy_addr inst into release/store if
|
|
// we have a non-address only type.
|
|
SILValue value = rvi->getOperand();
|
|
|
|
// If we have an address only type, do nothing.
|
|
SILType type = value->getType();
|
|
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
|
|
type.isLoadable(*fn) &&
|
|
"Copy Value can only be called on loadable types.");
|
|
|
|
if (!shouldExpandShim(fn, type.getObjectType()))
|
|
return false;
|
|
|
|
auto &typeLowering = fn->getTypeLowering(type);
|
|
typeLowering.emitLoweredCopyValueMostDerivedDescendents(builder,
|
|
rvi->getLoc(), value);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << " Expanding: " << *rvi);
|
|
|
|
++NumExpand;
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Top Level Driver
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static bool processFunction(SILFunction &fn) {
|
|
bool changed = false;
|
|
for (auto &block : fn) {
|
|
auto ii = block.begin(), ie = block.end();
|
|
while (ii != ie) {
|
|
SILInstruction *inst = &*ii;
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *inst);
|
|
|
|
if (auto *cai = dyn_cast<CopyAddrInst>(inst))
|
|
if (expandCopyAddr(cai)) {
|
|
++ii;
|
|
cai->eraseFromParent();
|
|
changed = true;
|
|
continue;
|
|
}
|
|
|
|
if (auto *dai = dyn_cast<DestroyAddrInst>(inst))
|
|
if (expandDestroyAddr(dai)) {
|
|
++ii;
|
|
dai->eraseFromParent();
|
|
changed = true;
|
|
continue;
|
|
}
|
|
|
|
if (auto *rvi = dyn_cast<RetainValueInst>(inst))
|
|
if (expandRetainValue(rvi)) {
|
|
++ii;
|
|
rvi->eraseFromParent();
|
|
changed = true;
|
|
continue;
|
|
}
|
|
|
|
if (auto *rvi = dyn_cast<ReleaseValueInst>(inst))
|
|
if (expandReleaseValue(rvi)) {
|
|
++ii;
|
|
rvi->eraseFromParent();
|
|
changed = true;
|
|
continue;
|
|
}
|
|
|
|
++ii;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Top Level Entrypoint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class SILLowerAggregate : public SILFunctionTransform {
|
|
|
|
/// The entry point to the transformation.
|
|
void run() override {
|
|
SILFunction *f = getFunction();
|
|
LLVM_DEBUG(llvm::dbgs() << "***** LowerAggregate on function: "
|
|
<< f->getName() << " *****\n");
|
|
bool changed = processFunction(*f);
|
|
if (changed) {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
|
|
}
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createLowerAggregateInstrs() {
|
|
return new SILLowerAggregate();
|
|
}
|