mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1447 lines
51 KiB
C++
1447 lines
51 KiB
C++
//===------------- Outliner.cpp - Outlining Transformations ---------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-outliner"
|
|
|
|
#include "swift/AST/ASTMangler.h"
|
|
#include "swift/AST/ConformanceLookup.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Demangling/Demangler.h"
|
|
#include "swift/Demangling/ManglingMacros.h"
|
|
#include "swift/SIL/ApplySite.h"
|
|
#include "swift/SIL/BasicBlockDatastructures.h"
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SIL/DebugUtils.h"
|
|
#include "swift/SIL/DynamicCasts.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
|
|
#include "llvm/ADT/BitVector.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace swift;
|
|
|
|
llvm::cl::opt<std::string> DumpFuncsBeforeOutliner(
|
|
"sil-dump-functions-before-outliner", llvm::cl::init(""),
|
|
llvm::cl::desc(
|
|
"Break before running each function pass on a particular function"));
|
|
|
|
namespace {
|
|
|
|
class OutlinerMangler : public Mangle::ASTMangler {
|
|
/// The kind of method bridged.
|
|
enum MethodKind : unsigned {
|
|
BridgedProperty,
|
|
BridgedProperty_Consuming,
|
|
BridgedPropertyAddress,
|
|
BridgedMethod,
|
|
};
|
|
|
|
llvm::BitVector *IsParameterBridged;
|
|
llvm::BitVector *IsParameterGuaranteed;
|
|
SILDeclRef MethodDecl;
|
|
MethodKind Kind;
|
|
bool IsReturnBridged;
|
|
|
|
public:
|
|
/// Create an mangler for an outlined bridged method.
|
|
OutlinerMangler(SILDeclRef Method, llvm::BitVector *ParameterBridged,
|
|
llvm::BitVector *IsParameterGuaranteed, bool ReturnBridged)
|
|
: ASTMangler(Method.getASTContext()), IsParameterBridged(ParameterBridged),
|
|
IsParameterGuaranteed(IsParameterGuaranteed), MethodDecl(Method),
|
|
Kind(BridgedMethod), IsReturnBridged(ReturnBridged) {}
|
|
|
|
/// Create an mangler for an outlined bridged property.
|
|
OutlinerMangler(SILDeclRef Method, bool IsAddress, bool ConsumesValue)
|
|
: ASTMangler(Method.getASTContext()), IsParameterBridged(nullptr), IsParameterGuaranteed(nullptr),
|
|
MethodDecl(Method),
|
|
Kind(IsAddress ? BridgedPropertyAddress
|
|
: (ConsumesValue ? BridgedProperty_Consuming
|
|
: BridgedProperty)),
|
|
IsReturnBridged(true) {}
|
|
|
|
std::string mangle();
|
|
|
|
private:
|
|
char getMethodKindMangling() {
|
|
switch (Kind) {
|
|
case BridgedProperty:
|
|
return 'p';
|
|
case BridgedProperty_Consuming:
|
|
return 'o';
|
|
case BridgedPropertyAddress:
|
|
return 'a';
|
|
case BridgedMethod:
|
|
return 'm';
|
|
}
|
|
llvm_unreachable("unhandled kind");
|
|
}
|
|
};
|
|
} // end anonymous namespace.
|
|
|
|
std::string OutlinerMangler::mangle() {
|
|
beginManglingWithoutPrefix();
|
|
|
|
appendOperator(MethodDecl.mangle());
|
|
|
|
llvm::SmallString<128> Buffer;
|
|
llvm::raw_svector_ostream Out(Buffer);
|
|
|
|
Out << getMethodKindMangling();
|
|
if (IsParameterBridged) {
|
|
for (unsigned Idx = 0, E = IsParameterBridged->size(); Idx != E; ++Idx) {
|
|
Out << (IsParameterBridged->test(Idx) ? 'b' : 'n');
|
|
// NOTE: We must keep owned as having nothing here to preserve ABI since
|
|
// mangling is part of ABI.
|
|
Out << (IsParameterGuaranteed->test(Idx) ? "g" : "");
|
|
}
|
|
}
|
|
Out << (IsReturnBridged ? 'b' : 'n');
|
|
Out << '_';
|
|
|
|
appendOperator("Te", Buffer);
|
|
return finalize();
|
|
}
|
|
|
|
namespace {
|
|
|
|
class OutlinePattern {
|
|
protected:
|
|
SILOptFunctionBuilder &FuncBuilder;
|
|
InstModCallbacks callbacks;
|
|
DeadEndBlocks *deBlocks;
|
|
|
|
public:
|
|
OutlinePattern(SILOptFunctionBuilder &FuncBuilder,
|
|
InstModCallbacks callbacks,
|
|
DeadEndBlocks *deBlocks)
|
|
: FuncBuilder(FuncBuilder), callbacks(callbacks), deBlocks(deBlocks) {}
|
|
|
|
/// Match the instruction sequence.
|
|
virtual bool matchInstSequence(SILBasicBlock::iterator I) = 0;
|
|
|
|
/// Outline the matched instruction sequence.
|
|
///
|
|
/// If a new outlined function is created return the function. If the outlined
|
|
/// function already existed return null.
|
|
/// Returns the last instruction of the matched sequence after the
|
|
/// replacement.
|
|
virtual std::pair<SILFunction *, SILBasicBlock::iterator>
|
|
outline(SILModule &M) = 0;
|
|
|
|
virtual std::string getOutlinedFunctionName() = 0;
|
|
|
|
virtual ~OutlinePattern() {}
|
|
};
|
|
|
|
/// Get the bridgeToObjectiveC witness for the type.
|
|
static SILDeclRef getBridgeToObjectiveC(CanType NativeType) {
|
|
auto &Ctx = NativeType->getASTContext();
|
|
auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
|
|
if (!Proto)
|
|
return SILDeclRef();
|
|
auto ConformanceRef = lookupConformance(NativeType, Proto);
|
|
if (ConformanceRef.isInvalid())
|
|
return SILDeclRef();
|
|
|
|
auto Conformance = ConformanceRef.getConcrete();
|
|
// bridgeToObjectiveC
|
|
DeclName Name(Ctx, Ctx.Id_bridgeToObjectiveC, llvm::ArrayRef<Identifier>());
|
|
auto *Requirement = dyn_cast_or_null<FuncDecl>(
|
|
Proto->getSingleRequirement(Name));
|
|
if (!Requirement)
|
|
return SILDeclRef();
|
|
|
|
auto Witness = Conformance->getWitnessDecl(Requirement);
|
|
return SILDeclRef(Witness);
|
|
}
|
|
|
|
/// Get the _unconditionallyBridgeFromObjectiveC witness for the type.
|
|
SILDeclRef getBridgeFromObjectiveC(CanType NativeType) {
|
|
auto &Ctx = NativeType->getASTContext();
|
|
auto Proto = Ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
|
|
if (!Proto)
|
|
return SILDeclRef();
|
|
auto ConformanceRef = lookupConformance(NativeType, Proto);
|
|
if (ConformanceRef.isInvalid())
|
|
return SILDeclRef();
|
|
auto Conformance = ConformanceRef.getConcrete();
|
|
// _unconditionallyBridgeFromObjectiveC
|
|
DeclName Name(Ctx, Ctx.getIdentifier("_unconditionallyBridgeFromObjectiveC"),
|
|
llvm::ArrayRef(Identifier()));
|
|
auto *Requirement = dyn_cast_or_null<FuncDecl>(
|
|
Proto->getSingleRequirement(Name));
|
|
if (!Requirement)
|
|
return SILDeclRef();
|
|
|
|
auto Witness = Conformance->getWitnessDecl(Requirement);
|
|
return SILDeclRef(Witness);
|
|
}
|
|
|
|
struct SwitchInfo {
|
|
SwitchEnumInst *SwitchEnum = nullptr;
|
|
SILBasicBlock *SomeBB = nullptr;
|
|
SILBasicBlock *NoneBB = nullptr;
|
|
BranchInst *Br = nullptr;
|
|
};
|
|
|
|
/// Pattern for a bridged property call.
|
|
///
|
|
/// bb7:
|
|
/// %30 = unchecked_take_enum_data_addr %19 : $*Optional<UITextField>, #Optional.some!enumelt
|
|
/// %31 = load %30 : $*UITextField
|
|
/// strong_retain %31 : $UITextField
|
|
/// %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
/// %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
/// switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
|
|
///
|
|
/// bb8(%36 : $NSString):
|
|
/// // function_ref static String._unconditionallyBridgeFromObjectiveC(_:)
|
|
/// %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
/// %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
|
|
/// %39 = metatype $@thin String.Type
|
|
/// %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
/// %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
|
|
/// br bb10(%41 : $Optional<String>)
|
|
///
|
|
/// bb9:
|
|
/// %43 = enum $Optional<String>, #Optional.none!enumelt
|
|
/// br bb10(%43 : $Optional<String>)
|
|
///
|
|
/// bb10(%45 : $Optional<String>):
|
|
class BridgedProperty : public OutlinePattern {
|
|
std::string OutlinedName;
|
|
SingleValueInstruction *FirstInst; // A load or class_method
|
|
SILBasicBlock *StartBB;
|
|
SwitchInfo switchInfo;
|
|
ObjCMethodInst *ObjCMethod;
|
|
SILInstruction *Release;
|
|
ApplyInst *PropApply;
|
|
SILInstruction *UnpairedRelease; // A release_value | destroy_value following
|
|
// the apply which isn't paired to
|
|
// load [copy] | strong_retain first value.
|
|
|
|
public:
|
|
bool matchInstSequence(SILBasicBlock::iterator I) override;
|
|
|
|
std::pair<SILFunction *, SILBasicBlock::iterator>
|
|
outline(SILModule &M) override;
|
|
|
|
BridgedProperty(SILOptFunctionBuilder &FuncBuilder,
|
|
InstModCallbacks callbacks,
|
|
DeadEndBlocks *deBlocks)
|
|
: OutlinePattern(FuncBuilder, callbacks, deBlocks) {
|
|
clearState();
|
|
}
|
|
|
|
BridgedProperty(const BridgedProperty&) = delete;
|
|
BridgedProperty& operator=(const BridgedProperty&) = delete;
|
|
|
|
virtual ~BridgedProperty() {}
|
|
|
|
std::string getOutlinedFunctionName() override;
|
|
|
|
private:
|
|
bool matchMethodCall(SILBasicBlock::iterator, LoadInst *);
|
|
CanSILFunctionType getOutlinedFunctionType(SILModule &M);
|
|
void clearState();
|
|
};
|
|
}
|
|
|
|
void BridgedProperty::clearState() {
|
|
FirstInst = nullptr;
|
|
StartBB = nullptr;
|
|
switchInfo = SwitchInfo();
|
|
ObjCMethod = nullptr;
|
|
Release = nullptr;
|
|
PropApply = nullptr;
|
|
UnpairedRelease = nullptr;
|
|
OutlinedName.clear();
|
|
}
|
|
|
|
std::string BridgedProperty::getOutlinedFunctionName() {
|
|
if (OutlinedName.empty()) {
|
|
OutlinerMangler Mangler(ObjCMethod->getMember(), isa<LoadInst>(FirstInst),
|
|
UnpairedRelease);
|
|
OutlinedName = Mangler.mangle();
|
|
}
|
|
return OutlinedName;
|
|
}
|
|
|
|
/// Returns the outlined function type.
|
|
///
|
|
/// This depends on the first instruction we matched. Either we matched a load
|
|
/// or we started the match at the class method instruction.
|
|
///
|
|
/// load %30 : *UITextField:
|
|
/// (@in_guaranteed InstanceType) -> (@owned Optional<BridgedInstanceType>)
|
|
/// objc_method %31 : UITextField
|
|
/// (@unowned InstanceType) -> (@owned Optional<BridgedInstanceType>)
|
|
///
|
|
CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) {
|
|
SmallVector<SILParameterInfo, 4> Parameters;
|
|
if (auto *Load = dyn_cast<LoadInst>(FirstInst))
|
|
Parameters.push_back(
|
|
SILParameterInfo(Load->getType().getASTType(),
|
|
ParameterConvention::Indirect_In_Guaranteed));
|
|
else
|
|
Parameters.push_back(SILParameterInfo(
|
|
cast<ObjCMethodInst>(FirstInst)->getOperand()->getType().getASTType(),
|
|
UnpairedRelease ? ParameterConvention::Direct_Owned
|
|
: ParameterConvention::Direct_Unowned));
|
|
SmallVector<SILResultInfo, 4> Results;
|
|
|
|
Results.push_back(SILResultInfo(
|
|
switchInfo.Br->getArg(0)->getType().getASTType(),
|
|
ResultConvention::Owned));
|
|
auto ExtInfo = SILFunctionType::ExtInfo::getThin();
|
|
auto FunctionType = SILFunctionType::get(
|
|
nullptr, ExtInfo, SILCoroutineKind::None,
|
|
ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, Results,
|
|
std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext());
|
|
return FunctionType;
|
|
}
|
|
|
|
static void eraseBlock(SILBasicBlock *block) {
|
|
for (SILInstruction &inst : *block) {
|
|
inst.replaceAllUsesOfAllResultsWithUndef();
|
|
}
|
|
block->eraseFromParent();
|
|
}
|
|
|
|
std::pair<SILFunction *, SILBasicBlock::iterator>
|
|
BridgedProperty::outline(SILModule &M) {
|
|
// Get the function type.
|
|
auto FunctionType = getOutlinedFunctionType(M);
|
|
|
|
std::string nameTmp = getOutlinedFunctionName();
|
|
auto name = M.allocateCopy(nameTmp);
|
|
|
|
auto *Fun = FuncBuilder.getOrCreateFunction(
|
|
ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare,
|
|
IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed,
|
|
IsNotRuntimeAccessible);
|
|
bool NeedsDefinition = Fun->empty();
|
|
|
|
if (Release) {
|
|
// Move the release after the call.
|
|
Release->moveBefore(StartBB->getTerminator());
|
|
}
|
|
|
|
// [StartBB]
|
|
// / \
|
|
// [NoneBB] [SomeBB]
|
|
// \ /
|
|
// [OldMergeBB]
|
|
//
|
|
// Split to:
|
|
//
|
|
// [StartBB]
|
|
// |
|
|
// [OutlinedEntryBB] }
|
|
// / \ }
|
|
// [NoneBB] [SomeBB] } outlined
|
|
// \ / }
|
|
// [OldMergeBB] }
|
|
// |
|
|
// [NewTailBB]
|
|
//
|
|
auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(FirstInst));
|
|
auto *OldMergeBB = switchInfo.Br->getDestBB();
|
|
auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin());
|
|
if (deBlocks) {
|
|
deBlocks->updateForNewBlock(NewTailBB);
|
|
}
|
|
// Call the outlined function.
|
|
{
|
|
SILBuilder Builder(StartBB);
|
|
auto Loc = FirstInst->getLoc();
|
|
SILValue FunRef(Builder.createFunctionRef(Loc, Fun));
|
|
SILValue Apply(
|
|
Builder.createApply(Loc, FunRef, SubstitutionMap(),
|
|
{FirstInst->getOperand(0)}));
|
|
Builder.createBranch(Loc, NewTailBB);
|
|
OldMergeBB->getArgument(0)->replaceAllUsesWith(Apply);
|
|
}
|
|
|
|
if (!NeedsDefinition) {
|
|
// Delete the outlined instructions/blocks.
|
|
if (Release)
|
|
Release->eraseFromParent();
|
|
eraseBlock(OutlinedEntryBB);
|
|
eraseBlock(switchInfo.NoneBB);
|
|
eraseBlock(switchInfo.SomeBB);
|
|
eraseBlock(OldMergeBB);
|
|
return std::make_pair(nullptr, std::prev(StartBB->end()));
|
|
}
|
|
|
|
if (!OutlinedEntryBB->getParent()->hasOwnership())
|
|
Fun->setOwnershipEliminated();
|
|
|
|
Fun->setInlineStrategy(NoInline);
|
|
|
|
// Move the blocks into the new function.
|
|
Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin());
|
|
Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, Fun->begin());
|
|
Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, Fun->begin());
|
|
Fun->moveBlockFromOtherFunction(OutlinedEntryBB, Fun->begin());
|
|
|
|
// Create the function argument and return.
|
|
auto *Load = dyn_cast<LoadInst>(FirstInst);
|
|
SILBuilder Builder(FirstInst);
|
|
if (Load) {
|
|
OutlinedEntryBB->createFunctionArgument(Load->getOperand()->getType());
|
|
auto *NewLoad =
|
|
Builder.createLoad(Load->getLoc(), OutlinedEntryBB->getArgument(0),
|
|
Load->getOwnershipQualifier());
|
|
Load->replaceAllUsesWith(NewLoad);
|
|
Load->eraseFromParent();
|
|
} else {
|
|
OutlinedEntryBB->createFunctionArgument(
|
|
FirstInst->getOperand(0)->getType());
|
|
auto *Arg = OutlinedEntryBB->getArgument(0);
|
|
FirstInst->setOperand(0, Arg);
|
|
if (UnpairedRelease)
|
|
UnpairedRelease->setOperand(0, Arg);
|
|
PropApply->setArgument(0, Arg);
|
|
}
|
|
Builder.setInsertionPoint(OldMergeBB);
|
|
Builder.createReturn(ObjCMethod->getLoc(), OldMergeBB->getArgument(0));
|
|
return std::make_pair(Fun, std::prev(StartBB->end()));
|
|
}
|
|
|
|
#define ADVANCE_ITERATOR_OR_RETURN_FALSE(It) \
|
|
do { \
|
|
if (It->getParent()->end() == ++It) \
|
|
return false; \
|
|
} while (0);
|
|
|
|
static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst,
|
|
SILValue SwitchOperand) {
|
|
auto *SwitchEnum = dyn_cast<SwitchEnumInst>(Inst);
|
|
if (!SwitchEnum || SwitchEnum->getNumCases() != 2 ||
|
|
SwitchEnum->getOperand() != SwitchOperand)
|
|
return false;
|
|
|
|
auto *SwitchBB = SwitchEnum->getParent();
|
|
SILBasicBlock *SomeBB = SwitchEnum->getCase(0).second;
|
|
SILBasicBlock *NoneBB = SwitchEnum->getCase(1).second;
|
|
if (NoneBB->getSinglePredecessorBlock() != SwitchBB)
|
|
return false;
|
|
if (SomeBB->getSinglePredecessorBlock() != SwitchBB)
|
|
return false;
|
|
if (NoneBB->args_size() == 1)
|
|
std::swap(NoneBB, SomeBB);
|
|
if (SomeBB->args_size() != 1 || NoneBB->args_size() != 0)
|
|
return false;
|
|
|
|
// bb9:
|
|
// %43 = enum $Optional<String>, #Optional.none!enumelt
|
|
auto It = NoneBB->begin();
|
|
auto *NoneEnum = dyn_cast<EnumInst>(It);
|
|
if (!NoneEnum || NoneEnum->hasOperand() || !NoneEnum->hasOneUse())
|
|
return false;
|
|
|
|
// br bb10(%43 : $Optional<String>)
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Br1 = dyn_cast<BranchInst>(It);
|
|
if (!Br1 || Br1->getNumArgs() != 1 || Br1->getArg(0) != NoneEnum)
|
|
return false;
|
|
auto *MergeBB = Br1->getDestBB();
|
|
|
|
// bb8(%36 : $NSString):
|
|
It = SomeBB->begin();
|
|
auto *SomeBBArg = SomeBB->getArgument(0);
|
|
if (!SomeBBArg->hasOneUse())
|
|
return false;
|
|
|
|
// %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
auto *FunRef = dyn_cast<FunctionRefInst>(It);
|
|
if (!FunRef || !FunRef->hasOneUse())
|
|
return false;
|
|
|
|
// %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *SomeEnum = dyn_cast<EnumInst>(It);
|
|
if (!SomeEnum || !SomeEnum->hasOperand() || SomeEnum->getOperand() != SomeBBArg)
|
|
return false;
|
|
size_t numSomeEnumUses = std::distance(SomeEnum->use_begin(), SomeEnum->use_end());
|
|
if (numSomeEnumUses > 2)
|
|
return false;
|
|
|
|
// %39 = metatype $@thin String.Type
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Metatype = dyn_cast<MetatypeInst>(It);
|
|
if (!Metatype || !Metatype->hasOneUse())
|
|
return false;
|
|
|
|
// %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Apply = dyn_cast<ApplyInst>(It);
|
|
if (!Apply || !Apply->hasOneUse() || Apply->getCallee() != FunRef ||
|
|
Apply->getNumArguments() != 2 || Apply->getArgument(0) != SomeEnum ||
|
|
Apply->getArgument(1) != Metatype ||
|
|
Apply->getSubstCalleeType()->getNumResults() != 1)
|
|
return false;
|
|
if (Apply->getSubstCalleeType()->getSingleResult().getConvention() !=
|
|
ResultConvention::Owned)
|
|
return false;
|
|
|
|
// Check that we call the _unconditionallyBridgeFromObjectiveC witness.
|
|
auto NativeType = Apply->getType().getASTType();
|
|
auto *BridgeFun = FunRef->getReferencedFunction();
|
|
// Not every type conforms to the ObjectiveCBridgeable protocol in such a case
|
|
// getBridgeFromObjectiveC returns SILDeclRef().
|
|
auto bridgeWitness = getBridgeFromObjectiveC(NativeType);
|
|
if (bridgeWitness == SILDeclRef() ||
|
|
BridgeFun->getName() != bridgeWitness.mangle())
|
|
return false;
|
|
|
|
// %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Enum3 = dyn_cast<EnumInst>(It);
|
|
if (!Enum3 || !Enum3->hasOneUse() || !Enum3->hasOperand() ||
|
|
Enum3->getOperand() != Apply)
|
|
return false;
|
|
|
|
if (numSomeEnumUses == 2) {
|
|
// [release_value | destroy_value] %38 : $Optional<NSString>
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
bool hasOwnership = It->getFunction()->hasOwnership();
|
|
if (hasOwnership) {
|
|
auto *DVI = dyn_cast<DestroyValueInst>(It);
|
|
if (!DVI || DVI->getOperand() != SomeEnum)
|
|
return false;
|
|
} else {
|
|
auto *RVI = dyn_cast<ReleaseValueInst>(It);
|
|
if (!RVI || RVI->getOperand() != SomeEnum)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// br bb10(%41 : $Optional<String>)
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Br = dyn_cast<BranchInst>(It);
|
|
if (!Br || Br->getDestBB() != MergeBB || Br->getNumArgs() != 1 ||
|
|
Br->getArg(0) != Enum3)
|
|
return false;
|
|
|
|
SI.SwitchEnum = SwitchEnum;
|
|
SI.SomeBB = SomeBB;
|
|
SI.NoneBB = NoneBB;
|
|
SI.Br = Br;
|
|
return true;
|
|
}
|
|
|
|
bool BridgedProperty::matchMethodCall(SILBasicBlock::iterator It,
|
|
LoadInst *Load) {
|
|
// Matches:
|
|
// %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
// %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
// switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
|
|
//
|
|
// bb8(%36 : $NSString):
|
|
// %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
// %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
|
|
// %39 = metatype $@thin String.Type
|
|
// %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
// %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
|
|
// br bb10(%41 : $Optional<String>)
|
|
//
|
|
// bb9:
|
|
// %43 = enum $Optional<String>, #Optional.none!enumelt
|
|
// br bb10(%43 : $Optional<String>)
|
|
//
|
|
// bb10(%45 : $Optional<String>):
|
|
//
|
|
|
|
// %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign
|
|
ObjCMethod = dyn_cast<ObjCMethodInst>(It);
|
|
SILValue Instance =
|
|
FirstInst != ObjCMethod ? FirstInst : ObjCMethod->getOperand();
|
|
if (!ObjCMethod || !ObjCMethod->hasOneUse() ||
|
|
ObjCMethod->getOperand() != Instance ||
|
|
ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
|
|
ObjCMethod->getType().castTo<SILFunctionType>()->isPolymorphic() ||
|
|
ObjCMethod->getType().castTo<SILFunctionType>()->hasOpenedExistential())
|
|
return false;
|
|
|
|
// %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
PropApply = dyn_cast<ApplyInst>(It);
|
|
if (!PropApply || PropApply->getCallee() != ObjCMethod ||
|
|
PropApply->getNumArguments() != 1 ||
|
|
PropApply->getArgument(0) != Instance || !PropApply->hasOneUse())
|
|
return false;
|
|
|
|
if (Load) {
|
|
// In OSSA, there will be a destroy_value matching the earlier load [copy].
|
|
// In non-ossa, there will be a release matching the earlier retain. The
|
|
// only user of the retained value is the unowned objective-c method
|
|
// consumer.
|
|
unsigned NumUses = 0;
|
|
Release = nullptr;
|
|
bool hasOwnership = Load->getFunction()->hasOwnership();
|
|
for (auto *Use : Load->getUses()) {
|
|
++NumUses;
|
|
SILInstruction *R;
|
|
if (hasOwnership) {
|
|
R = dyn_cast<DestroyValueInst>(Use->getUser());
|
|
} else {
|
|
R = dyn_cast<StrongReleaseInst>(Use->getUser());
|
|
}
|
|
if (R) {
|
|
if (!Release) {
|
|
Release = R;
|
|
} else {
|
|
Release = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!Release)
|
|
return false;
|
|
if (hasOwnership) {
|
|
if (NumUses != 3)
|
|
return false;
|
|
} else {
|
|
if (NumUses != 4)
|
|
return false;
|
|
}
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
if (Release != &*It)
|
|
return false;
|
|
}
|
|
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
if (auto *dvi = dyn_cast<DestroyValueInst>(*&It)) {
|
|
if (Load)
|
|
return false;
|
|
if (dvi->getOperand() != Instance)
|
|
return false;
|
|
UnpairedRelease = dvi;
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
}
|
|
|
|
// Don't outline in the outlined function.
|
|
if (ObjCMethod->getFunction()->getName() == getOutlinedFunctionName())
|
|
return false;
|
|
|
|
// switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8,
|
|
// case #Optional.none!enumelt: bb9
|
|
return matchSwitch(switchInfo, &*It, PropApply);
|
|
}
|
|
|
|
bool BridgedProperty::matchInstSequence(SILBasicBlock::iterator It) {
|
|
// Matches:
|
|
// [ optionally:
|
|
// %31 = load %30 : $*UITextField or %31 = load [copy] %30 : $*UITextField
|
|
// strong_retain %31 : $UITextField
|
|
// ]
|
|
// %33 = objc_method %31 : $UITextField, #UITextField.text!getter.foreign : (UITextField) -> () -> String?, $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
// %34 = apply %33(%31) : $@convention(objc_method) (UITextField) -> @autoreleased Optional<NSString>
|
|
// switch_enum %34 : $Optional<NSString>, case #Optional.some!enumelt: bb8, case #Optional.none!enumelt: bb9
|
|
//
|
|
// bb8(%36 : $NSString):
|
|
// %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
// %38 = enum $Optional<NSString>, #Optional.some!enumelt, %36 : $NSString
|
|
// %39 = metatype $@thin String.Type
|
|
// %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String
|
|
// %41 = enum $Optional<String>, #Optional.some!enumelt, %40 : $String
|
|
// br bb10(%41 : $Optional<String>)
|
|
//
|
|
// bb9:
|
|
// %43 = enum $Optional<String>, #Optional.none!enumelt
|
|
// br bb10(%43 : $Optional<String>)
|
|
//
|
|
// bb10(%45 : $Optional<String>):
|
|
|
|
clearState();
|
|
|
|
// %31 = load %30 : $*UITextField
|
|
auto *Load = dyn_cast<LoadInst>(It);
|
|
|
|
// Otherwise, trying matching from the method call.
|
|
if (!Load) {
|
|
// Try to match without the load/strong_retain prefix.
|
|
auto *CMI = dyn_cast<ObjCMethodInst>(It);
|
|
if (!CMI || CMI->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
|
|
CMI->getType().castTo<SILFunctionType>()->isPolymorphic() ||
|
|
CMI->getType().castTo<SILFunctionType>()->hasOpenedExistential())
|
|
return false;
|
|
FirstInst = CMI;
|
|
} else
|
|
FirstInst = Load;
|
|
|
|
StartBB = FirstInst->getParent();
|
|
|
|
if (Load) {
|
|
if (Load->getFunction()->hasOwnership()) {
|
|
if (Load->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
|
|
return false;
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
} else {
|
|
// strong_retain %31 : $UITextField
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
auto *Retain = dyn_cast<StrongRetainInst>(It);
|
|
if (!Retain || Retain->getOperand() != Load)
|
|
return false;
|
|
ADVANCE_ITERATOR_OR_RETURN_FALSE(It);
|
|
}
|
|
}
|
|
|
|
if (!matchMethodCall(It, Load))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
/// Match a bridged argument.
|
|
///
|
|
///
|
|
/// %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF
|
|
/// %16 = apply %15(%14) :
|
|
/// $@convention(method) (@guaranteed String) -> @owned NSString
|
|
/// %17 = enum $Optional<NSString>, #Optional.some!enumelt, %16 : $NSString
|
|
/// release_value %14 : $String
|
|
///
|
|
/// apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional<NSString> ...) ->
|
|
/// release_value %17 : $Optional<NSString>
|
|
///
|
|
/// NOTE: If release_value %14 is found, our outlined function will have an
|
|
/// owned convention for self. Otherwise, if we do not find it, we will have a
|
|
/// guaranteed one.
|
|
class BridgedArgument {
|
|
public:
|
|
FunctionRefInst *BridgeFun;
|
|
ApplyInst *BridgeCall;
|
|
EnumInst *OptionalResult;
|
|
SILValue BridgedValue;
|
|
SILInstruction *ReleaseAfterBridge;
|
|
SILInstruction *ReleaseArgAfterCall;
|
|
unsigned Idx = 0;
|
|
|
|
// Matched bridged argument.
|
|
BridgedArgument(unsigned Idx, FunctionRefInst *F, ApplyInst *A, EnumInst *E,
|
|
SILInstruction *R0, SILInstruction *R1)
|
|
: BridgeFun(F), BridgeCall(A), OptionalResult(E),
|
|
BridgedValue(FullApplySite(A).getSelfArgument()),
|
|
ReleaseAfterBridge(R0), ReleaseArgAfterCall(R1), Idx(Idx) {
|
|
assert(!R0 || isa<ReleaseValueInst>(R0) || isa<DestroyValueInst>(R0));
|
|
}
|
|
|
|
/// Invalid argument constructor.
|
|
BridgedArgument()
|
|
: BridgeFun(nullptr), BridgeCall(nullptr), OptionalResult(nullptr),
|
|
ReleaseAfterBridge(nullptr), ReleaseArgAfterCall(nullptr), Idx(0) {}
|
|
|
|
static BridgedArgument match(unsigned ArgIdx, SILValue Arg, ApplyInst *AI,
|
|
DeadEndBlocks *deBlocks);
|
|
|
|
operator bool() const { return BridgeFun != nullptr; }
|
|
SILValue bridgedValue() { return BridgedValue; }
|
|
|
|
bool isGuaranteed() const { return ReleaseAfterBridge == nullptr; }
|
|
ParameterConvention getConvention() const {
|
|
if (isGuaranteed())
|
|
return ParameterConvention::Direct_Guaranteed;
|
|
return ParameterConvention::Direct_Owned;
|
|
}
|
|
|
|
void eraseFromParent();
|
|
|
|
/// Move the bridged argument sequence to the bridged call block.
|
|
/// Precondition: The bridged call has already been moved to the outlined
|
|
/// function.
|
|
void transferTo(SILValue BridgedValueFunArg, ApplyInst *BridgedCall);
|
|
};
|
|
}
|
|
|
|
void BridgedArgument::transferTo(SILValue BridgedValue,
|
|
ApplyInst *BridgedCall) {
|
|
assert(BridgedCall->getParent() != BridgeFun->getParent());
|
|
// Move the instructions to the bridged call that we have already moved and
|
|
// update the uses of the bridge value by the function argument value passed
|
|
// to this function.
|
|
auto *DestBB = BridgedCall->getParent();
|
|
DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeFun);
|
|
DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), BridgeCall);
|
|
BridgeCall->setArgument(0, BridgedValue);
|
|
DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), OptionalResult);
|
|
if (ReleaseAfterBridge) {
|
|
DestBB->moveTo(SILBasicBlock::iterator(BridgedCall), ReleaseAfterBridge);
|
|
ReleaseAfterBridge->setOperand(0, BridgedValue);
|
|
}
|
|
auto AfterCall = std::next(SILBasicBlock::iterator(BridgedCall));
|
|
DestBB->moveTo(SILBasicBlock::iterator(AfterCall), ReleaseArgAfterCall);
|
|
}
|
|
|
|
void BridgedArgument::eraseFromParent() {
|
|
if (ReleaseAfterBridge)
|
|
ReleaseAfterBridge->eraseFromParent();
|
|
ReleaseArgAfterCall->eraseFromParent();
|
|
OptionalResult->eraseFromParent();
|
|
BridgeCall->eraseFromParent();
|
|
BridgeFun->eraseFromParent();
|
|
}
|
|
|
|
static SILInstruction *findReleaseOf(SILValue releasedValue,
|
|
SILBasicBlock::iterator from,
|
|
SILBasicBlock::iterator to) {
|
|
bool hasOwnership = releasedValue->getFunction()->hasOwnership();
|
|
while (from != to) {
|
|
if (hasOwnership) {
|
|
auto destroy = dyn_cast<DestroyValueInst>(&*from);
|
|
if (destroy && destroy->getOperand() == releasedValue)
|
|
return destroy;
|
|
} else {
|
|
auto release = dyn_cast<ReleaseValueInst>(&*from);
|
|
if (release && release->getOperand() == releasedValue)
|
|
return release;
|
|
}
|
|
++from;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg,
|
|
ApplyInst *AI, DeadEndBlocks *deBlocks) {
|
|
// Match
|
|
// %15 = function_ref @$SSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF
|
|
// %16 = apply %15(%14) : $@convention(method) (@guaranteed String) -> @owned NSString
|
|
// %17 = enum $Optional<NSString>, #Optional.some!enumelt, %16 : $NSString
|
|
// [release_value | destroy_value] %14 : $String
|
|
// ...
|
|
// apply %objcMethod(%17, ...) : $@convention(objc_method) (Optional<NSString>...) ->
|
|
// release_value ...
|
|
// [release_value | destroy_value] %17 : $Optional<NSString>
|
|
//
|
|
auto *Enum = dyn_cast<EnumInst>(Arg);
|
|
if (!Enum || !Enum->hasOperand())
|
|
return BridgedArgument();
|
|
|
|
if (SILBasicBlock::iterator(Enum) == Enum->getParent()->begin())
|
|
return BridgedArgument();
|
|
auto *BridgeCall =
|
|
dyn_cast<ApplyInst>(std::prev(SILBasicBlock::iterator(Enum)));
|
|
if (!BridgeCall || BridgeCall->getNumArguments() != 1 ||
|
|
Enum->getOperand() != BridgeCall || !BridgeCall->hasOneUse() ||
|
|
!FullApplySite(BridgeCall).hasSelfArgument())
|
|
return BridgedArgument();
|
|
|
|
auto &selfArg = FullApplySite(BridgeCall).getSelfArgumentOperand();
|
|
auto selfConvention =
|
|
FullApplySite(BridgeCall).getArgumentConvention(selfArg);
|
|
if (selfConvention != SILArgumentConvention::Direct_Guaranteed &&
|
|
selfConvention != SILArgumentConvention::Direct_Owned)
|
|
return BridgedArgument();
|
|
|
|
auto BridgedValue = BridgeCall->getArgument(0);
|
|
auto Next = std::next(SILBasicBlock::iterator(Enum));
|
|
if (Next == Enum->getParent()->end())
|
|
return BridgedArgument();
|
|
|
|
// Make sure that if we have a bridged value release that it is on the bridged
|
|
// value.
|
|
if (Enum->getParent() != AI->getParent())
|
|
return BridgedArgument();
|
|
|
|
bool hasOwnership = AI->getFunction()->hasOwnership();
|
|
auto *BridgedValueRelease =
|
|
findReleaseOf(BridgedValue, std::next(SILBasicBlock::iterator(Enum)),
|
|
SILBasicBlock::iterator(AI));
|
|
assert(!BridgedValueRelease ||
|
|
(hasOwnership && isa<DestroyValueInst>(BridgedValueRelease)) ||
|
|
isa<ReleaseValueInst>(BridgedValueRelease));
|
|
if (BridgedValueRelease && BridgedValueRelease->getOperand(0) != BridgedValue)
|
|
return BridgedArgument();
|
|
|
|
if (SILBasicBlock::iterator(BridgeCall) == BridgeCall->getParent()->begin())
|
|
return BridgedArgument();
|
|
|
|
auto *FunRef =
|
|
dyn_cast<FunctionRefInst>(std::prev(SILBasicBlock::iterator(BridgeCall)));
|
|
if (!FunRef || !FunRef->hasOneUse() || BridgeCall->getCallee() != FunRef)
|
|
return BridgedArgument();
|
|
|
|
SILInstruction *ReleaseAfter = nullptr;
|
|
for (auto *Use : Enum->getUses()) {
|
|
if (Use->getUser() == AI)
|
|
continue;
|
|
|
|
// The enum must only have two uses the release and the apply.
|
|
if (ReleaseAfter)
|
|
return BridgedArgument();
|
|
|
|
if (hasOwnership) {
|
|
ReleaseAfter = dyn_cast<DestroyValueInst>(Use->getUser());
|
|
} else {
|
|
ReleaseAfter = dyn_cast<ReleaseValueInst>(Use->getUser());
|
|
}
|
|
if (!ReleaseAfter)
|
|
return BridgedArgument();
|
|
}
|
|
|
|
// Make sure we are calling the actual bridge witness.
|
|
auto NativeType = BridgedValue->getType().getASTType();
|
|
auto *BridgeFun = FunRef->getReferencedFunction();
|
|
// Not every type conforms to the ObjectiveCBridgeable protocol in such a case
|
|
// getBridgeToObjectiveC returns SILDeclRef().
|
|
auto bridgeWitness = getBridgeToObjectiveC(NativeType);
|
|
if (bridgeWitness == SILDeclRef() ||
|
|
BridgeFun->getName() != bridgeWitness.mangle())
|
|
return BridgedArgument();
|
|
|
|
if (hasOwnership && !BridgedValueRelease) {
|
|
SmallVector<Operand *> newUses{&AI->getOperandRef(ArgIdx)};
|
|
if (!areUsesWithinValueLifetime(BridgedValue, newUses, deBlocks)) {
|
|
return BridgedArgument();
|
|
}
|
|
}
|
|
return BridgedArgument(ArgIdx, FunRef, BridgeCall, Enum, BridgedValueRelease,
|
|
ReleaseAfter);
|
|
}
|
|
|
|
namespace {
|
|
// Match the return value bridging pattern.
|
|
// switch_enum %20 : $Optional<NSString>, case #O.some: bb1, case #O.none: bb2
|
|
//
|
|
// bb1(%23 : $NSString):
|
|
// %24 = function_ref @_unconditionallyBridgeFromObjectiveC
|
|
// %25 = enum $Optional<NSString>, #Optional.some!enumelt, %23 : $NSString
|
|
// %26 = metatype $@thin String.Type
|
|
// %27 = apply %24(%25, %26)
|
|
// %28 = enum $Optional<String>, #Optional.some!enumelt, %27 : $String
|
|
// br bb3(%28 : $Optional<String>)
|
|
//
|
|
// bb2:
|
|
// %30 = enum $Optional<String>, #Optional.none!enumelt
|
|
// br bb3(%30 : $Optional<String>)
|
|
//
|
|
// bb3(%32 : $Optional<String>):
|
|
class BridgedReturn {
|
|
DeadEndBlocks *deBlocks;
|
|
SwitchInfo switchInfo;
|
|
public:
|
|
BridgedReturn(DeadEndBlocks *deBlocks) : deBlocks(deBlocks) {}
|
|
|
|
bool match(ApplyInst *BridgedCall) {
|
|
switchInfo = SwitchInfo();
|
|
auto *SwitchBB = BridgedCall->getParent();
|
|
return matchSwitch(switchInfo, SwitchBB->getTerminator(), BridgedCall);
|
|
}
|
|
|
|
operator bool() { return switchInfo.SomeBB != nullptr; }
|
|
|
|
CanType getReturnType() {
|
|
return switchInfo.Br->getArg(0)->getType().getASTType();
|
|
}
|
|
|
|
/// Outline the return value bridging blocks.
|
|
void outline(SILFunction *Fun, ApplyInst *NewOutlinedCall);
|
|
};
|
|
}
|
|
|
|
void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) {
|
|
// Outline the bridged return result blocks.
|
|
// switch_enum %20 : $Optional<NSString>, case #O.some: bb1, case #O.none: bb2
|
|
//
|
|
// bb1(%23 : $NSString):
|
|
// %24 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveC
|
|
// %25 = enum $Optional<NSString>, #Optional.some!enumelt, %23 : $NSString
|
|
// %26 = metatype $@thin String.Type
|
|
// %27 = apply %24(%25, %26)
|
|
// %28 = enum $Optional<String>, #Optional.some!enumelt, %27 : $String
|
|
// br bb3(%28 : $Optional<String>)
|
|
//
|
|
// bb2:
|
|
// %30 = enum $Optional<String>, #Optional.none!enumelt
|
|
// br bb3(%30 : $Optional<String>)
|
|
//
|
|
// bb3(%32 : $Optional<String>):
|
|
|
|
auto *StartBB = switchInfo.SwitchEnum->getParent();
|
|
auto *OutlinedEntryBB = StartBB->split(SILBasicBlock::iterator(switchInfo.SwitchEnum));
|
|
auto *OldMergeBB = switchInfo.Br->getDestBB();
|
|
auto *NewTailBB = OldMergeBB->split(OldMergeBB->begin());
|
|
if (deBlocks) {
|
|
deBlocks->updateForNewBlock(NewTailBB);
|
|
}
|
|
auto Loc = switchInfo.SwitchEnum->getLoc();
|
|
|
|
{
|
|
SILBuilder Builder(StartBB);
|
|
Builder.createBranch(Loc, NewTailBB);
|
|
OldMergeBB->getArgument(0)->replaceAllUsesWith(NewOutlinedCall);
|
|
}
|
|
|
|
// Outlined function already existed. Just delete instructions and wire up
|
|
// blocks.
|
|
if (!Fun) {
|
|
eraseBlock(OutlinedEntryBB);
|
|
eraseBlock(switchInfo.NoneBB);
|
|
eraseBlock(switchInfo.SomeBB);
|
|
eraseBlock(OldMergeBB);
|
|
return;
|
|
}
|
|
|
|
// Move the blocks into the new function.
|
|
assert(Fun->begin() != Fun->end() &&
|
|
"The entry block must already have been created");
|
|
SILBasicBlock *EntryBB = &*Fun->begin();
|
|
Fun->moveBlockFromOtherFunction(OldMergeBB, Fun->begin());
|
|
Fun->moveBlockAfter(OldMergeBB, EntryBB);
|
|
auto InsertPt = SILFunction::iterator(OldMergeBB);
|
|
Fun->moveBlockFromOtherFunction(OutlinedEntryBB, InsertPt);
|
|
Fun->moveBlockFromOtherFunction(switchInfo.NoneBB, InsertPt);
|
|
Fun->moveBlockFromOtherFunction(switchInfo.SomeBB, InsertPt);
|
|
|
|
SILBuilder Builder (EntryBB);
|
|
Builder.createBranch(Loc, OutlinedEntryBB);
|
|
|
|
Builder.setInsertionPoint(OldMergeBB);
|
|
Builder.createReturn(Loc, OldMergeBB->getArgument(0));
|
|
}
|
|
|
|
namespace {
|
|
class ObjCMethodCall : public OutlinePattern {
|
|
ObjCMethodInst *ObjCMethod;
|
|
ApplyInst *BridgedCall;
|
|
SmallVector<BridgedArgument, 4> BridgedArguments;
|
|
std::string OutlinedName;
|
|
llvm::BitVector IsBridgedArgument;
|
|
llvm::BitVector IsGuaranteedArgument;
|
|
::BridgedReturn BridgedReturn;
|
|
public:
|
|
bool matchInstSequence(SILBasicBlock::iterator I) override;
|
|
|
|
std::pair<SILFunction *, SILBasicBlock::iterator>
|
|
outline(SILModule &M) override;
|
|
|
|
ObjCMethodCall(SILOptFunctionBuilder &FuncBuilder,
|
|
InstModCallbacks callbacks,
|
|
DeadEndBlocks *deBlocks)
|
|
: OutlinePattern(FuncBuilder, callbacks, deBlocks), BridgedReturn(deBlocks) {}
|
|
~ObjCMethodCall();
|
|
|
|
private:
|
|
void clearState();
|
|
std::string getOutlinedFunctionName() override;
|
|
CanSILFunctionType getOutlinedFunctionType(SILModule &M);
|
|
};
|
|
}
|
|
|
|
ObjCMethodCall::~ObjCMethodCall() {
|
|
clearState();
|
|
}
|
|
|
|
void ObjCMethodCall::clearState() {
|
|
ObjCMethod = nullptr;
|
|
BridgedCall = nullptr;
|
|
BridgedArguments.clear();
|
|
OutlinedName.clear();
|
|
IsBridgedArgument.clear();
|
|
IsGuaranteedArgument.clear();
|
|
}
|
|
|
|
std::pair<SILFunction *, SILBasicBlock::iterator>
|
|
ObjCMethodCall::outline(SILModule &M) {
|
|
|
|
auto FunctionType = getOutlinedFunctionType(M);
|
|
std::string nameTmp = getOutlinedFunctionName();
|
|
auto name = M.allocateCopy(nameTmp);
|
|
|
|
auto *Fun = FuncBuilder.getOrCreateFunction(
|
|
ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare,
|
|
IsNotTransparent, IsSerialized, IsNotDynamic, IsNotDistributed,
|
|
IsNotRuntimeAccessible);
|
|
bool NeedsDefinition = Fun->empty();
|
|
|
|
// Call the outlined function.
|
|
ApplyInst *OutlinedCall;
|
|
{
|
|
SILBuilder Builder(BridgedCall);
|
|
|
|
auto Loc = BridgedCall->getLoc();
|
|
SILValue FunRef(Builder.createFunctionRef(Loc, Fun));
|
|
|
|
// Collect the arguments for the apply.
|
|
SmallVector<SILValue, 8> Args;
|
|
unsigned OrigSigIdx = 0;
|
|
unsigned BridgedArgIdx = 0;
|
|
for (auto Arg : BridgedCall->getArguments()) {
|
|
if (BridgedArgIdx < BridgedArguments.size() &&
|
|
BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
|
|
auto bridgedArgValue = BridgedArguments[BridgedArgIdx].bridgedValue();
|
|
Args.push_back(bridgedArgValue);
|
|
++BridgedArgIdx;
|
|
} else {
|
|
// Otherwise, use the original type convention.
|
|
Args.push_back(Arg);
|
|
}
|
|
++OrigSigIdx;
|
|
}
|
|
OutlinedCall = Builder.createApply(Loc, FunRef, SubstitutionMap(), Args);
|
|
if (!BridgedCall->use_empty() && !BridgedReturn)
|
|
BridgedCall->replaceAllUsesWith(OutlinedCall);
|
|
}
|
|
|
|
// Outlined function already exists. Only need to delete basic blocks/instructions.
|
|
if (!NeedsDefinition) {
|
|
if (BridgedReturn)
|
|
BridgedReturn.outline(nullptr, OutlinedCall);
|
|
BridgedCall->eraseFromParent();
|
|
ObjCMethod->eraseFromParent();
|
|
// Remove bridged argument code.
|
|
for (auto Arg : BridgedArguments)
|
|
Arg.eraseFromParent();
|
|
SILBasicBlock::iterator I(OutlinedCall);
|
|
return std::make_pair(Fun, I);
|
|
}
|
|
|
|
if (!ObjCMethod->getFunction()->hasOwnership())
|
|
Fun->setOwnershipEliminated();
|
|
|
|
Fun->setInlineStrategy(NoInline);
|
|
|
|
// Create the entry block.
|
|
auto *EntryBB = Fun->createBasicBlock();
|
|
|
|
// Move the bridged call.
|
|
EntryBB->moveTo(EntryBB->end(), ObjCMethod);
|
|
EntryBB->moveTo(EntryBB->end(), BridgedCall);
|
|
|
|
// Create the arguments.
|
|
unsigned OrigSigIdx = 0;
|
|
unsigned BridgedArgIdx = 0;
|
|
SILValue LastArg;
|
|
for (auto Arg : BridgedCall->getArguments()) {
|
|
if (BridgedArgIdx < BridgedArguments.size() &&
|
|
BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
|
|
auto &BridgedArg = BridgedArguments[BridgedArgIdx];
|
|
auto *FunArg =
|
|
EntryBB->createFunctionArgument(BridgedArg.bridgedValue()->getType());
|
|
BridgedArg.transferTo(FunArg, BridgedCall);
|
|
++BridgedArgIdx;
|
|
} else {
|
|
auto *FunArg = EntryBB->createFunctionArgument(Arg->getType());
|
|
BridgedCall->setArgument(OrigSigIdx, FunArg);
|
|
LastArg = FunArg;
|
|
}
|
|
++OrigSigIdx;
|
|
}
|
|
|
|
// Set the method lookup's target.
|
|
ObjCMethod->setOperand(LastArg);
|
|
|
|
// Create the return and optionally move the bridging code.
|
|
if (!BridgedReturn) {
|
|
SILBuilder Builder(EntryBB);
|
|
Builder.createReturn(BridgedCall->getLoc(), BridgedCall);
|
|
} else {
|
|
BridgedReturn.outline(Fun, OutlinedCall);
|
|
}
|
|
|
|
SILBasicBlock::iterator I(OutlinedCall);
|
|
return std::make_pair(Fun, I);
|
|
}
|
|
|
|
std::string ObjCMethodCall::getOutlinedFunctionName() {
|
|
if (OutlinedName.empty()) {
|
|
OutlinerMangler Mangler(ObjCMethod->getMember(), &IsBridgedArgument,
|
|
&IsGuaranteedArgument, BridgedReturn);
|
|
OutlinedName = Mangler.mangle();
|
|
}
|
|
return OutlinedName;
|
|
}
|
|
|
|
bool ObjCMethodCall::matchInstSequence(SILBasicBlock::iterator I) {
|
|
clearState();
|
|
|
|
ObjCMethod = dyn_cast<ObjCMethodInst>(I);
|
|
if (!ObjCMethod ||
|
|
ObjCMethod->getFunction()->getLoweredFunctionType()->isPolymorphic() ||
|
|
ObjCMethod->getType().castTo<SILFunctionType>()->isPolymorphic() ||
|
|
ObjCMethod->getType().castTo<SILFunctionType>()->hasOpenedExistential())
|
|
return false;
|
|
|
|
auto *Use = ObjCMethod->getSingleUse();
|
|
if (!Use)
|
|
return false;
|
|
BridgedCall = dyn_cast<ApplyInst>(Use->getUser());
|
|
if (!BridgedCall ||
|
|
(!BridgedCall->hasOneUse() && !BridgedCall->use_empty()) ||
|
|
ObjCMethod->getParent() != BridgedCall->getParent())
|
|
return false;
|
|
|
|
// Collect bridged parameters.
|
|
unsigned Idx = 0;
|
|
IsBridgedArgument.resize(BridgedCall->getNumArguments(), false);
|
|
IsGuaranteedArgument.resize(BridgedCall->getNumArguments(), false);
|
|
// Map from owned values whose bridged versions we've seen in the apply to
|
|
// the index in the apply at which they appear or UINT_MAX if we've seen them
|
|
// more than once (which means we've already nulled out all the
|
|
// BridgedArguments' ReleaseAfterBridge).
|
|
llvm::DenseMap<SILValue, unsigned> seenOwnedBridgedValues;
|
|
for (auto &Param : BridgedCall->getArgumentOperands()) {
|
|
unsigned CurIdx = Idx++;
|
|
|
|
// Look for Optional<NSFoo> type.
|
|
auto Ty = Param.get()->getType().getOptionalObjectType();
|
|
if (!Ty)
|
|
continue;
|
|
|
|
// Can't handle AnyObject. The concrete class type can be different since we
|
|
// are passing 'id'. To make this work we would have to mangle the type into
|
|
// the function name.
|
|
if (Ty.isAnyObject())
|
|
continue;
|
|
|
|
auto BridgedArg =
|
|
BridgedArgument::match(CurIdx, Param.get(), BridgedCall, deBlocks);
|
|
if (!BridgedArg)
|
|
continue;
|
|
|
|
BridgedArguments.push_back(BridgedArg);
|
|
IsBridgedArgument.set(CurIdx);
|
|
if (BridgedArg.isGuaranteed()) {
|
|
IsGuaranteedArgument.set(CurIdx);
|
|
continue;
|
|
}
|
|
// Record that this owned value was used at CurIdx.
|
|
auto pair =
|
|
seenOwnedBridgedValues.insert({BridgedArg.BridgedValue, CurIdx});
|
|
auto firstSighting = pair.second;
|
|
if (firstSighting) {
|
|
continue;
|
|
}
|
|
// This owned value was already seen. Convert the current argument to
|
|
// guaranteed and the previous argument as well if necessary.
|
|
auto iterator = pair.first;
|
|
if (iterator->second != UINT_MAX) {
|
|
// This is the _second_ time the value has been seen. Clear the previous
|
|
// occurrence's ReleaseAfterBridge and sink the destroy after the apply.
|
|
BridgedArguments[iterator->second].ReleaseAfterBridge->moveAfter(
|
|
BridgedCall);
|
|
BridgedArguments[iterator->second].ReleaseAfterBridge = nullptr;
|
|
IsGuaranteedArgument.set(iterator->second);
|
|
iterator->second = UINT_MAX;
|
|
}
|
|
BridgedArguments[CurIdx].ReleaseAfterBridge = nullptr;
|
|
IsGuaranteedArgument.set(CurIdx);
|
|
}
|
|
|
|
// Try to match a bridged return value.
|
|
BridgedReturn.match(BridgedCall);
|
|
|
|
// Don't outline inside the outlined function.
|
|
auto OutlinedName = getOutlinedFunctionName();
|
|
auto CurrentName = ObjCMethod->getFunction()->getName();
|
|
if (CurrentName == OutlinedName)
|
|
return false;
|
|
|
|
// Don't outline if we created an outlined function without the bridged result
|
|
// from the outlined function with the bridged result (only the suffix is
|
|
// different: MethodNameTem...n_ vs MethodNameTem...b_).
|
|
if (OutlinedName.size() == CurrentName.size() &&
|
|
CurrentName.starts_with(
|
|
StringRef(OutlinedName.c_str(), OutlinedName.size() - 2)))
|
|
return false;
|
|
|
|
return !BridgedArguments.empty();
|
|
}
|
|
|
|
CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) {
|
|
auto FunTy = BridgedCall->getSubstCalleeType();
|
|
SmallVector<SILParameterInfo, 4> Parameters;
|
|
unsigned OrigSigIdx = 0;
|
|
unsigned BridgedArgIdx = 0;
|
|
for (auto &ParamInfo : FunTy->getParameters()) {
|
|
// Either use the bridged type passing it @owned.
|
|
if (BridgedArgIdx < BridgedArguments.size() &&
|
|
BridgedArguments[BridgedArgIdx].Idx == OrigSigIdx) {
|
|
auto convention = BridgedArguments[BridgedArgIdx].getConvention();
|
|
Parameters.push_back(SILParameterInfo(BridgedArguments[BridgedArgIdx]
|
|
.bridgedValue()
|
|
->getType()
|
|
.getASTType(),
|
|
convention));
|
|
++BridgedArgIdx;
|
|
} else {
|
|
// Otherwise, use the original type convention.
|
|
Parameters.push_back(ParamInfo);
|
|
}
|
|
++OrigSigIdx;
|
|
}
|
|
|
|
auto ExtInfo = SILFunctionType::ExtInfo::getThin();
|
|
|
|
SmallVector<SILResultInfo, 4> Results;
|
|
// If we don't have a bridged return we changed from @autoreleased to @owned
|
|
// if there is a result.
|
|
if (!BridgedReturn) {
|
|
if (FunTy->getNumResults()) {
|
|
auto OrigResultInfo = FunTy->getSingleResult();
|
|
Results.push_back(SILResultInfo(OrigResultInfo.getInterfaceType(),
|
|
OrigResultInfo.getConvention() ==
|
|
ResultConvention::Autoreleased
|
|
? ResultConvention::Owned
|
|
: OrigResultInfo.getConvention()));
|
|
}
|
|
} else {
|
|
// Otherwise, we used the bridged return type.
|
|
Results.push_back(
|
|
SILResultInfo(BridgedReturn.getReturnType(), ResultConvention::Owned));
|
|
}
|
|
auto FunctionType = SILFunctionType::get(
|
|
nullptr, ExtInfo, SILCoroutineKind::None,
|
|
ParameterConvention::Direct_Unowned, Parameters, {}, Results,
|
|
std::nullopt, SubstitutionMap(), SubstitutionMap(), M.getASTContext());
|
|
return FunctionType;
|
|
}
|
|
|
|
namespace {
|
|
/// A collection of outlineable patterns.
|
|
class OutlinePatterns {
|
|
BridgedProperty BridgedPropertyPattern;
|
|
ObjCMethodCall ObjCMethodCallPattern;
|
|
|
|
public:
|
|
/// Try matching an outlineable pattern from the current instruction.
|
|
OutlinePattern *tryToMatch(SILBasicBlock::iterator CurInst) {
|
|
if (BridgedPropertyPattern.matchInstSequence(CurInst))
|
|
return &BridgedPropertyPattern;
|
|
|
|
if (ObjCMethodCallPattern.matchInstSequence(CurInst))
|
|
return &ObjCMethodCallPattern;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
OutlinePatterns(SILOptFunctionBuilder &FuncBuilder,
|
|
InstModCallbacks callbacks,
|
|
DeadEndBlocks *deBlocks)
|
|
: BridgedPropertyPattern(FuncBuilder, callbacks, deBlocks),
|
|
ObjCMethodCallPattern(FuncBuilder, callbacks, deBlocks) {}
|
|
~OutlinePatterns() {}
|
|
|
|
OutlinePatterns(const OutlinePatterns&) = delete;
|
|
OutlinePatterns& operator=(const OutlinePatterns) = delete;
|
|
};
|
|
} // end anonymous namespace.
|
|
|
|
|
|
/// Perform outlining on the function and return any newly created outlined
|
|
/// functions.
|
|
bool tryOutline(SILOptFunctionBuilder &FuncBuilder, SILFunction *Fun,
|
|
SmallVectorImpl<SILFunction *> &FunctionsAdded,
|
|
InstModCallbacks callbacks = InstModCallbacks(),
|
|
DeadEndBlocks *deBlocks = nullptr) {
|
|
BasicBlockWorklist Worklist(Fun->getEntryBlock());
|
|
OutlinePatterns patterns(FuncBuilder, callbacks, deBlocks);
|
|
bool changed = false;
|
|
|
|
// Traverse the function.
|
|
while (SILBasicBlock *CurBlock = Worklist.pop()) {
|
|
SILBasicBlock::iterator CurInst = CurBlock->begin();
|
|
|
|
// Go over the instructions trying to match and replace patterns.
|
|
while (CurInst != CurBlock->end()) {
|
|
if (OutlinePattern *match = patterns.tryToMatch(CurInst)) {
|
|
SILFunction *F;
|
|
SILBasicBlock::iterator LastInst;
|
|
std::tie(F, LastInst) = match->outline(Fun->getModule());
|
|
if (F)
|
|
FunctionsAdded.push_back(F);
|
|
CurInst = LastInst;
|
|
assert(LastInst->getParent() == CurBlock);
|
|
changed = true;
|
|
} else if (isa<TermInst>(CurInst)) {
|
|
for (SILBasicBlock *succ : CurBlock->getSuccessors()) {
|
|
Worklist.pushIfNotVisited(succ);
|
|
}
|
|
++CurInst;
|
|
} else {
|
|
++CurInst;
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class Outliner : public SILFunctionTransform {
|
|
public:
|
|
Outliner() { }
|
|
|
|
void run() override {
|
|
auto *Fun = getFunction();
|
|
|
|
// Only outline if we optimize for size.
|
|
if (!Fun->optimizeForSize())
|
|
return;
|
|
|
|
// Dump function if requested.
|
|
if (DumpFuncsBeforeOutliner.size() &&
|
|
Fun->getName().contains(DumpFuncsBeforeOutliner)) {
|
|
Fun->dump();
|
|
}
|
|
|
|
DeadEndBlocksAnalysis *deBlocksAnalysis =
|
|
PM->getAnalysis<DeadEndBlocksAnalysis>();
|
|
DeadEndBlocks *deBlocks = deBlocksAnalysis->get(Fun);
|
|
InstModCallbacks callbacks;
|
|
|
|
SILOptFunctionBuilder FuncBuilder(*this);
|
|
SmallVector<SILFunction *, 16> FunctionsAdded;
|
|
bool Changed = false;
|
|
|
|
if (Fun->hasOwnership()) {
|
|
Changed =
|
|
tryOutline(FuncBuilder, Fun, FunctionsAdded, callbacks, deBlocks);
|
|
} else {
|
|
Changed = tryOutline(FuncBuilder, Fun, FunctionsAdded);
|
|
}
|
|
|
|
if (!FunctionsAdded.empty()) {
|
|
// Notify the pass manager of any new functions we outlined.
|
|
for (auto *AddedFunc : FunctionsAdded) {
|
|
addFunctionToPassManagerWorklist(AddedFunc, nullptr);
|
|
}
|
|
}
|
|
|
|
if (Changed) {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
|
|
}
|
|
}
|
|
};
|
|
|
|
} //end anonymous namespace.
|
|
|
|
SILTransform *swift::createOutliner() {
|
|
return new Outliner();
|
|
}
|