mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
905 lines
34 KiB
C++
905 lines
34 KiB
C++
//===--- ArraySemantic.cpp - Wrapper around array semantic calls. ---------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
|
|
#include "swift/SIL/DebugUtils.h"
|
|
#include "swift/SIL/InstructionUtils.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Determine which kind of array semantics function this is.
|
|
ArrayCallKind swift::getArraySemanticsKind(SILFunction *f) {
|
|
ArrayCallKind Kind = ArrayCallKind::kNone;
|
|
|
|
for (auto &Attrs : f->getSemanticsAttrs()) {
|
|
auto Tmp =
|
|
llvm::StringSwitch<ArrayCallKind>(Attrs)
|
|
.Case("array.props.isNativeTypeChecked",
|
|
ArrayCallKind::kArrayPropsIsNativeTypeChecked)
|
|
.Case("array.init", ArrayCallKind::kArrayInit)
|
|
.Case("array.init.empty", ArrayCallKind::kArrayInitEmpty)
|
|
.Case("array.uninitialized", ArrayCallKind::kArrayUninitialized)
|
|
.Case("array.uninitialized_intrinsic", ArrayCallKind::kArrayUninitializedIntrinsic)
|
|
.Case("array.finalize_intrinsic", ArrayCallKind::kArrayFinalizeIntrinsic)
|
|
.Case("array.check_subscript", ArrayCallKind::kCheckSubscript)
|
|
.Case("array.check_index", ArrayCallKind::kCheckIndex)
|
|
.Case("array.get_count", ArrayCallKind::kGetCount)
|
|
.Case("array.get_capacity", ArrayCallKind::kGetCapacity)
|
|
.Case("array.get_element", ArrayCallKind::kGetElement)
|
|
.Case("array.make_mutable", ArrayCallKind::kMakeMutable)
|
|
.Case("array.end_mutation", ArrayCallKind::kEndMutation)
|
|
.Case("array.get_element_address",
|
|
ArrayCallKind::kGetElementAddress)
|
|
.Case("array.mutate_unknown", ArrayCallKind::kMutateUnknown)
|
|
.Case("array.reserve_capacity_for_append",
|
|
ArrayCallKind::kReserveCapacityForAppend)
|
|
.Case("array.withUnsafeMutableBufferPointer",
|
|
ArrayCallKind::kWithUnsafeMutableBufferPointer)
|
|
.Case("array.append_contentsOf", ArrayCallKind::kAppendContentsOf)
|
|
.Case("array.append_element", ArrayCallKind::kAppendElement)
|
|
.Default(ArrayCallKind::kNone);
|
|
if (Tmp != ArrayCallKind::kNone) {
|
|
assert(Kind == ArrayCallKind::kNone && "Multiple array semantic "
|
|
"strings?!");
|
|
Kind = Tmp;
|
|
}
|
|
}
|
|
|
|
return Kind;
|
|
}
|
|
|
|
static ParameterConvention
|
|
getSelfParameterConvention(ApplyInst *SemanticsCall) {
|
|
FunctionRefInst *FRI = cast<FunctionRefInst>(SemanticsCall->getCallee());
|
|
SILFunction *F = FRI->getInitiallyReferencedFunction();
|
|
auto FnTy = F->getLoweredFunctionType();
|
|
|
|
return FnTy->getSelfParameter().getConvention();
|
|
}
|
|
|
|
/// Make sure that all parameters are passed with a reference count
|
|
/// neutral parameter convention except for self.
|
|
bool swift::ArraySemanticsCall::isValidSignature() {
|
|
assert(SemanticsCall && getKind() != ArrayCallKind::kNone &&
|
|
"Need an array semantic call");
|
|
FunctionRefInst *FRI = cast<FunctionRefInst>(SemanticsCall->getCallee());
|
|
SILFunction *F = FRI->getInitiallyReferencedFunction();
|
|
auto FnTy = F->getLoweredFunctionType();
|
|
auto &Mod = F->getModule();
|
|
|
|
// Check whether we have a valid signature for semantic calls that we hoist.
|
|
switch (getKind()) {
|
|
// All other calls can be consider valid.
|
|
default: break;
|
|
case ArrayCallKind::kArrayPropsIsNativeTypeChecked: {
|
|
// @guaranteed/@owned Self
|
|
if (SemanticsCall->getNumArguments() != 1)
|
|
return false;
|
|
auto SelfConvention = FnTy->getSelfParameter().getConvention();
|
|
return SelfConvention == ParameterConvention::Direct_Guaranteed ||
|
|
SelfConvention == ParameterConvention::Direct_Owned;
|
|
}
|
|
case ArrayCallKind::kCheckIndex: {
|
|
// Int, @guaranteed/@owned Self
|
|
if (SemanticsCall->getNumArguments() != 2 ||
|
|
!SemanticsCall->getArgument(0)->getType().isTrivial(*F))
|
|
return false;
|
|
auto SelfConvention = FnTy->getSelfParameter().getConvention();
|
|
return SelfConvention == ParameterConvention::Direct_Guaranteed ||
|
|
SelfConvention == ParameterConvention::Direct_Owned;
|
|
}
|
|
case ArrayCallKind::kCheckSubscript: {
|
|
// Int, Bool, Self
|
|
unsigned numArgs = SemanticsCall->getNumArguments();
|
|
if (numArgs != 2 && numArgs != 3)
|
|
return false;
|
|
if (!SemanticsCall->getArgument(0)->getType().isTrivial(*F))
|
|
return false;
|
|
if (numArgs == 3 && !SemanticsCall->getArgument(1)->getType().isTrivial(*F))
|
|
return false;
|
|
auto SelfConvention = FnTy->getSelfParameter().getConvention();
|
|
return SelfConvention == ParameterConvention::Direct_Guaranteed ||
|
|
SelfConvention == ParameterConvention::Direct_Owned;
|
|
}
|
|
case ArrayCallKind::kMakeMutable: {
|
|
auto SelfConvention = FnTy->getSelfParameter().getConvention();
|
|
return SelfConvention == ParameterConvention::Indirect_Inout;
|
|
}
|
|
case ArrayCallKind::kArrayUninitialized: {
|
|
// Make sure that if we are a _adoptStorage call that our storage is
|
|
// uniquely referenced by us.
|
|
SILValue Arg0 = SemanticsCall->getArgument(0);
|
|
if (Arg0->getType().isExistentialType()) {
|
|
auto *AllocBufferAI = dyn_cast<ApplyInst>(Arg0);
|
|
if (!AllocBufferAI)
|
|
return false;
|
|
auto *AllocFn = AllocBufferAI->getReferencedFunctionOrNull();
|
|
if (!AllocFn || AllocFn->getName() != "swift_bufferAllocate" ||
|
|
!hasOneNonDebugUse(AllocBufferAI))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
case ArrayCallKind::kWithUnsafeMutableBufferPointer: {
|
|
SILFunctionConventions origConv(SemanticsCall->getOrigCalleeType(), Mod);
|
|
if (origConv.getNumIndirectSILResults() != 1
|
|
|| SemanticsCall->getNumArguments() != 3)
|
|
return false;
|
|
auto SelfConvention = FnTy->getSelfParameter().getConvention();
|
|
return SelfConvention == ParameterConvention::Indirect_Inout;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Match array semantic calls.
|
|
swift::ArraySemanticsCall::ArraySemanticsCall(SILValue V,
|
|
StringRef semanticName,
|
|
bool matchPartialName)
|
|
: SemanticsCall(nullptr) {
|
|
if (auto AI = dyn_cast<ApplyInst>(V))
|
|
initialize(AI, semanticName, matchPartialName);
|
|
}
|
|
|
|
/// Match array semantic calls.
|
|
swift::ArraySemanticsCall::ArraySemanticsCall(SILInstruction *I,
|
|
StringRef semanticName,
|
|
bool matchPartialName)
|
|
: SemanticsCall(nullptr) {
|
|
if (auto AI = dyn_cast<ApplyInst>(I))
|
|
initialize(AI, semanticName, matchPartialName);
|
|
}
|
|
|
|
/// Match array semantic calls.
|
|
swift::ArraySemanticsCall::ArraySemanticsCall(ApplyInst *AI,
|
|
StringRef semanticName,
|
|
bool matchPartialName)
|
|
: SemanticsCall(nullptr) {
|
|
initialize(AI, semanticName, matchPartialName);
|
|
}
|
|
|
|
void ArraySemanticsCall::initialize(ApplyInst *AI, StringRef semanticName,
|
|
bool matchPartialName) {
|
|
auto *fn = AI->getReferencedFunctionOrNull();
|
|
if (!fn)
|
|
return;
|
|
|
|
if (!(matchPartialName
|
|
? fn->hasSemanticsAttrThatStartsWith(semanticName)
|
|
: fn->hasSemanticsAttr(semanticName)))
|
|
return;
|
|
|
|
SemanticsCall = AI;
|
|
|
|
// Need a 'self' argument otherwise this is not a semantic call that
|
|
// we recognize.
|
|
if (getKind() < ArrayCallKind::kArrayInit && !hasSelf())
|
|
SemanticsCall = nullptr;
|
|
|
|
// A arguments must be passed reference count neutral except for self.
|
|
if (SemanticsCall && !isValidSignature())
|
|
SemanticsCall = nullptr;
|
|
}
|
|
|
|
/// Determine which kind of array semantics call this is.
|
|
ArrayCallKind swift::ArraySemanticsCall::getKind() const {
|
|
if (!SemanticsCall)
|
|
return ArrayCallKind::kNone;
|
|
|
|
auto F = cast<FunctionRefInst>(SemanticsCall->getCallee())
|
|
->getInitiallyReferencedFunction();
|
|
|
|
return getArraySemanticsKind(F);
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::hasSelf() const {
|
|
assert(SemanticsCall && "Must have a semantics call");
|
|
// Array.init and Array.uninitialized return 'self' @owned.
|
|
return SemanticsCall->getOrigCalleeType()->hasSelfParam();
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getSelf() const {
|
|
return SemanticsCall->getSelfArgument();
|
|
}
|
|
|
|
Operand &swift::ArraySemanticsCall::getSelfOperand() const {
|
|
return SemanticsCall->getSelfArgumentOperand();
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::hasGuaranteedSelf() const {
|
|
if (!hasSelf())
|
|
return false;
|
|
return getSelfParameterConvention(SemanticsCall) ==
|
|
ParameterConvention::Direct_Guaranteed;
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::hasGetElementDirectResult() const {
|
|
assert(getKind() == ArrayCallKind::kGetElement &&
|
|
"must be an array.get_element call");
|
|
bool DirectResult =
|
|
(SemanticsCall->getOrigCalleeConv().getNumIndirectSILResults() == 0);
|
|
assert((DirectResult && SemanticsCall->getNumArguments() == 4 ||
|
|
!DirectResult && SemanticsCall->getNumArguments() == 5) &&
|
|
"wrong number of array.get_element call arguments");
|
|
return DirectResult;
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getTypeCheckedArgument() const {
|
|
return SemanticsCall->getArgument(hasGetElementDirectResult() ? 1 : 2);
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getSubscriptCheckArgument() const {
|
|
return SemanticsCall->getArgument(hasGetElementDirectResult() ? 2 : 3);
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getIndex() const {
|
|
assert(SemanticsCall && "Must have a semantics call");
|
|
assert(SemanticsCall->getNumArguments() && "Must have arguments");
|
|
assert(getKind() == ArrayCallKind::kCheckSubscript ||
|
|
getKind() == ArrayCallKind::kCheckIndex ||
|
|
getKind() == ArrayCallKind::kGetElement ||
|
|
getKind() == ArrayCallKind::kGetElementAddress);
|
|
|
|
if (getKind() == ArrayCallKind::kGetElement)
|
|
return SemanticsCall->getArgument(hasGetElementDirectResult() ? 0 : 1);
|
|
|
|
return SemanticsCall->getArgument(0);
|
|
}
|
|
|
|
Optional<int64_t> swift::ArraySemanticsCall::getConstantIndex() const {
|
|
auto *IndexStruct = dyn_cast<StructInst>(getIndex());
|
|
if (!IndexStruct)
|
|
return None;
|
|
auto StructOpds = IndexStruct->getElements();
|
|
if (StructOpds.size() != 1)
|
|
return None;
|
|
auto *Literal = dyn_cast<IntegerLiteralInst>(StructOpds[0]);
|
|
if (!Literal)
|
|
return None;
|
|
|
|
auto Val = Literal->getValue();
|
|
if (Val.getNumWords()>1)
|
|
return None;
|
|
|
|
return Val.getSExtValue();
|
|
}
|
|
|
|
static bool canHoistArrayArgument(ApplyInst *SemanticsCall, SILValue Arr,
|
|
SILInstruction *InsertBefore,
|
|
DominanceInfo *DT) {
|
|
|
|
// We only know how to hoist inout, owned or guaranteed parameters.
|
|
auto Convention = getSelfParameterConvention(SemanticsCall);
|
|
if (Convention != ParameterConvention::Indirect_Inout &&
|
|
Convention != ParameterConvention::Direct_Owned &&
|
|
Convention != ParameterConvention::Direct_Guaranteed)
|
|
return false;
|
|
|
|
ValueBase *SelfVal = Arr;
|
|
auto *SelfBB = SelfVal->getParentBlock();
|
|
if (DT->dominates(SelfBB, InsertBefore->getParent()))
|
|
return true;
|
|
|
|
if (auto *Copy = dyn_cast<CopyValueInst>(SelfVal)) {
|
|
// look through one level
|
|
SelfVal = Copy->getOperand();
|
|
}
|
|
if (auto LI = dyn_cast<LoadInst>(SelfVal)) {
|
|
// Are we loading a value from an address in a struct defined at a point
|
|
// dominating the hoist point.
|
|
auto Val = LI->getOperand();
|
|
bool DoesNotDominate;
|
|
StructElementAddrInst *SEI;
|
|
while ((DoesNotDominate = !DT->dominates(Val->getParentBlock(),
|
|
InsertBefore->getParent())) &&
|
|
(SEI = dyn_cast<StructElementAddrInst>(Val)))
|
|
Val = SEI->getOperand();
|
|
return !DoesNotDominate;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::canHoist(SILInstruction *InsertBefore,
|
|
DominanceInfo *DT) const {
|
|
auto Kind = getKind();
|
|
switch (Kind) {
|
|
default:
|
|
break;
|
|
|
|
case ArrayCallKind::kCheckIndex:
|
|
case ArrayCallKind::kArrayPropsIsNativeTypeChecked:
|
|
case ArrayCallKind::kGetElementAddress:
|
|
case ArrayCallKind::kGetCount:
|
|
case ArrayCallKind::kGetCapacity:
|
|
return canHoistArrayArgument(SemanticsCall, getSelf(), InsertBefore, DT);
|
|
|
|
case ArrayCallKind::kGetElement:
|
|
// Not implemented yet.
|
|
return false;
|
|
|
|
case ArrayCallKind::kCheckSubscript:
|
|
if (SILValue IsNativeArg = getArrayPropertyIsNativeTypeChecked()) {
|
|
ArraySemanticsCall IsNative(IsNativeArg,
|
|
"array.props.isNativeTypeChecked", true);
|
|
if (!IsNative) {
|
|
// Do we have a constant parameter?
|
|
auto *SI = dyn_cast<StructInst>(IsNativeArg);
|
|
if (!SI)
|
|
return false;
|
|
if (!isa<IntegerLiteralInst>(SI->getOperand(0)))
|
|
return false;
|
|
} else if (!IsNative.canHoist(InsertBefore, DT))
|
|
// Otherwise, we must be able to hoist the function call.
|
|
return false;
|
|
}
|
|
return canHoistArrayArgument(SemanticsCall, getSelf(), InsertBefore, DT);
|
|
|
|
case ArrayCallKind::kMakeMutable:
|
|
case ArrayCallKind::kEndMutation:
|
|
return canHoistArrayArgument(SemanticsCall, getSelf(), InsertBefore, DT);
|
|
} // End switch.
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Copy the array self value to the insert point.
|
|
static SILValue copySelfValue(SILValue ArrayStructValue,
|
|
SILInstruction *InsertBefore, DominanceInfo *DT) {
|
|
auto *func = InsertBefore->getFunction();
|
|
if (DT->dominates(ArrayStructValue->getParentBlock(),
|
|
InsertBefore->getParent())) {
|
|
assert(!func->hasOwnership() ||
|
|
ArrayStructValue.getOwnershipKind() == OwnershipKind::Owned ||
|
|
ArrayStructValue.getOwnershipKind() == OwnershipKind::Guaranteed);
|
|
return ArrayStructValue;
|
|
}
|
|
|
|
assert(!func->hasOwnership() ||
|
|
ArrayStructValue.getOwnershipKind() == OwnershipKind::Owned);
|
|
|
|
SILValue Val;
|
|
if (auto *Load = dyn_cast<LoadInst>(ArrayStructValue)) {
|
|
Val = Load->getOperand();
|
|
} else {
|
|
auto *Copy = cast<CopyValueInst>(ArrayStructValue);
|
|
Val = cast<LoadInst>(Copy->getOperand())->getOperand();
|
|
}
|
|
auto *InsertPt = InsertBefore;
|
|
while (!DT->dominates(Val->getParentBlock(), InsertBefore->getParent())) {
|
|
auto *Inst = cast<StructElementAddrInst>(Val);
|
|
Inst->moveBefore(InsertPt);
|
|
Val = Inst->getOperand();
|
|
InsertPt = Inst;
|
|
}
|
|
|
|
if (!ArrayStructValue->getFunction()->hasOwnership()) {
|
|
return cast<LoadInst>(ArrayStructValue)->clone(InsertBefore);
|
|
}
|
|
if (auto *Load = dyn_cast<LoadInst>(ArrayStructValue)) {
|
|
return Load->clone(InsertBefore);
|
|
}
|
|
auto *Copy = cast<CopyValueInst>(ArrayStructValue);
|
|
auto Addr = cast<LoadInst>(Copy->getOperand())->getOperand();
|
|
return SILBuilderWithScope(InsertPt).createLoad(InsertPt->getLoc(), Addr,
|
|
LoadOwnershipQualifier::Copy);
|
|
}
|
|
|
|
static ApplyInst *hoistOrCopyCall(ApplyInst *AI, SILInstruction *InsertBefore,
|
|
bool LeaveOriginal, DominanceInfo *DT) {
|
|
if (!LeaveOriginal) {
|
|
AI->moveBefore(InsertBefore);
|
|
} else {
|
|
// Leave the original and 'hoist' a clone.
|
|
AI = cast<ApplyInst>(AI->clone(InsertBefore));
|
|
}
|
|
placeFuncRef(AI, DT);
|
|
return AI;
|
|
}
|
|
|
|
|
|
/// Hoist or copy the self argument of the semantics call.
|
|
/// Return the hoisted self argument.
|
|
static SILValue hoistOrCopySelf(ApplyInst *SemanticsCall,
|
|
SILInstruction *InsertBefore,
|
|
DominanceInfo *DT, bool LeaveOriginal) {
|
|
|
|
auto SelfConvention = getSelfParameterConvention(SemanticsCall);
|
|
|
|
assert((SelfConvention == ParameterConvention::Direct_Owned ||
|
|
SelfConvention == ParameterConvention::Direct_Guaranteed) &&
|
|
"Expect @owned or @guaranteed self");
|
|
|
|
auto Self = SemanticsCall->getSelfArgument();
|
|
bool IsOwnedSelf = SelfConvention == ParameterConvention::Direct_Owned;
|
|
auto *Func = SemanticsCall->getFunction();
|
|
|
|
// Emit matching release for owned self if we are moving the original call.
|
|
if (!LeaveOriginal && IsOwnedSelf) {
|
|
SILBuilderWithScope Builder(SemanticsCall);
|
|
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), Self);
|
|
}
|
|
auto NewArrayStructValue = copySelfValue(Self, InsertBefore, DT);
|
|
if (!Func->hasOwnership() && IsOwnedSelf) {
|
|
// Retain the array.
|
|
SILBuilderWithScope Builder(InsertBefore, SemanticsCall);
|
|
Builder.createRetainValue(SemanticsCall->getLoc(), NewArrayStructValue,
|
|
Builder.getDefaultAtomicity());
|
|
}
|
|
|
|
return NewArrayStructValue;
|
|
}
|
|
|
|
ApplyInst *swift::ArraySemanticsCall::hoistOrCopy(SILInstruction *InsertBefore,
|
|
DominanceInfo *DT,
|
|
bool LeaveOriginal) {
|
|
assert(canHoist(InsertBefore, DT) &&
|
|
"Must be able to hoist the semantics call");
|
|
|
|
auto Kind = getKind();
|
|
switch (Kind) {
|
|
case ArrayCallKind::kArrayPropsIsNativeTypeChecked:
|
|
case ArrayCallKind::kGetCount:
|
|
case ArrayCallKind::kGetCapacity: {
|
|
assert(SemanticsCall->getNumArguments() == 1 &&
|
|
"Expect 'self' parameter only");
|
|
|
|
auto HoistedSelf =
|
|
hoistOrCopySelf(SemanticsCall, InsertBefore, DT, LeaveOriginal);
|
|
|
|
auto *Call =
|
|
hoistOrCopyCall(SemanticsCall, InsertBefore, LeaveOriginal, DT);
|
|
Call->setSelfArgument(HoistedSelf);
|
|
return Call;
|
|
}
|
|
|
|
case ArrayCallKind::kCheckSubscript:
|
|
case ArrayCallKind::kCheckIndex: {
|
|
auto HoistedSelf =
|
|
hoistOrCopySelf(SemanticsCall, InsertBefore, DT, LeaveOriginal);
|
|
|
|
SILValue NewArrayProps;
|
|
if (SILValue IsNativeArg = getArrayPropertyIsNativeTypeChecked()) {
|
|
// Copy the array.props argument call.
|
|
ArraySemanticsCall IsNative(IsNativeArg,
|
|
"array.props.isNativeTypeChecked", true);
|
|
if (!IsNative) {
|
|
// Do we have a constant parameter?
|
|
auto *SI = dyn_cast<StructInst>(IsNativeArg);
|
|
assert(SI && isa<IntegerLiteralInst>(SI->getOperand(0)) &&
|
|
"Must have a constant parameter or an array.props.isNative call "
|
|
"as argument");
|
|
SI->moveBefore(&*DT->findNearestCommonDominator(
|
|
InsertBefore->getParent(), SI->getParent())
|
|
->begin());
|
|
auto *IL = cast<IntegerLiteralInst>(SI->getOperand(0));
|
|
IL->moveBefore(&*DT->findNearestCommonDominator(
|
|
InsertBefore->getParent(), IL->getParent())
|
|
->begin());
|
|
} else {
|
|
NewArrayProps = IsNative.copyTo(InsertBefore, DT);
|
|
}
|
|
|
|
// Replace all uses of the check subscript call by a use of the empty
|
|
// dependence. The check subscript call is no longer associated with
|
|
// another operation.
|
|
auto EmptyDep = SILBuilderWithScope(SemanticsCall)
|
|
.createStruct(SemanticsCall->getLoc(),
|
|
SemanticsCall->getType(), {});
|
|
SemanticsCall->replaceAllUsesWith(EmptyDep);
|
|
}
|
|
|
|
// Hoist the call.
|
|
auto Call = hoistOrCopyCall(SemanticsCall, InsertBefore, LeaveOriginal, DT);
|
|
Call->setSelfArgument(HoistedSelf);
|
|
|
|
if (NewArrayProps) {
|
|
// Set the array.props argument.
|
|
Call->setArgument(1, NewArrayProps);
|
|
}
|
|
|
|
|
|
return Call;
|
|
}
|
|
|
|
case ArrayCallKind::kMakeMutable:
|
|
case ArrayCallKind::kEndMutation: {
|
|
// Hoist the call.
|
|
auto Call = hoistOrCopyCall(SemanticsCall, InsertBefore, LeaveOriginal, DT);
|
|
return Call;
|
|
}
|
|
|
|
default:
|
|
llvm_unreachable("Don't know how to hoist this instruction");
|
|
break;
|
|
} // End switch.
|
|
}
|
|
|
|
void swift::ArraySemanticsCall::removeCall() {
|
|
if (getSelfParameterConvention(SemanticsCall) ==
|
|
ParameterConvention::Direct_Owned) {
|
|
SILBuilderWithScope Builder(SemanticsCall);
|
|
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), getSelf());
|
|
}
|
|
|
|
switch (getKind()) {
|
|
default: break;
|
|
case ArrayCallKind::kCheckSubscript:
|
|
if (!SemanticsCall->getType().isVoid()){
|
|
// Remove all uses with the empty tuple ().
|
|
auto EmptyDep = SILBuilderWithScope(SemanticsCall)
|
|
.createStruct(SemanticsCall->getLoc(),
|
|
SemanticsCall->getType(), {});
|
|
SemanticsCall->replaceAllUsesWith(EmptyDep);
|
|
}
|
|
break;
|
|
case ArrayCallKind::kGetElement: {
|
|
// Remove the matching isNativeTypeChecked and check_subscript call.
|
|
ArraySemanticsCall IsNative(getTypeCheckedArgument(),
|
|
"array.props.isNativeTypeChecked");
|
|
ArraySemanticsCall SubscriptCheck(getSubscriptCheckArgument(),
|
|
"array.check_subscript");
|
|
if (SubscriptCheck)
|
|
SubscriptCheck.removeCall();
|
|
|
|
// array.isNativeTypeChecked might be shared among several get_element
|
|
// calls. The last user should delete it.
|
|
if (IsNative && getSingleNonDebugUser((ApplyInst *)IsNative) ==
|
|
SemanticsCall) {
|
|
deleteAllDebugUses(IsNative);
|
|
(*IsNative).replaceAllUsesWithUndef();
|
|
IsNative.removeCall();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
SemanticsCall->eraseFromParent();
|
|
SemanticsCall = nullptr;
|
|
}
|
|
|
|
SILValue
|
|
swift::ArraySemanticsCall::getArrayPropertyIsNativeTypeChecked() const {
|
|
switch (getKind()) {
|
|
case ArrayCallKind::kCheckSubscript:
|
|
if (SemanticsCall->getNumArguments() == 3)
|
|
return SemanticsCall->getArgument(1);
|
|
return SILValue();
|
|
case ArrayCallKind::kGetElement:
|
|
return getTypeCheckedArgument();
|
|
default:
|
|
return SILValue();
|
|
}
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::doesNotChangeArray() const {
|
|
switch (getKind()) {
|
|
default: return false;
|
|
case ArrayCallKind::kArrayPropsIsNativeTypeChecked:
|
|
case ArrayCallKind::kCheckSubscript:
|
|
case ArrayCallKind::kCheckIndex:
|
|
case ArrayCallKind::kGetCount:
|
|
case ArrayCallKind::kGetCapacity:
|
|
case ArrayCallKind::kGetElement:
|
|
case ArrayCallKind::kEndMutation:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::mayHaveBridgedObjectElementType() const {
|
|
assert(hasSelf() && "Need self parameter");
|
|
|
|
auto Ty = getSelf()->getType();
|
|
if (auto BGT = Ty.getAs<BoundGenericStructType>()) {
|
|
// Check the array element type parameter.
|
|
bool isClass = true;
|
|
for (auto EltTy : BGT->getGenericArgs()) {
|
|
if (EltTy->isBridgeableObjectType())
|
|
return true;
|
|
isClass = false;
|
|
}
|
|
return isClass;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::canInlineEarly() const {
|
|
switch (getKind()) {
|
|
default:
|
|
return false;
|
|
case ArrayCallKind::kAppendContentsOf:
|
|
case ArrayCallKind::kReserveCapacityForAppend:
|
|
case ArrayCallKind::kAppendElement:
|
|
case ArrayCallKind::kArrayUninitializedIntrinsic:
|
|
// append(Element) calls other semantics functions. Therefore it's
|
|
// important that it's inlined by the early inliner (which is before all
|
|
// the array optimizations). Also, this semantics is only used to lookup
|
|
// Array.append(Element), so inlining it does not prevent any other
|
|
// optimization.
|
|
//
|
|
// Early inlining array.uninitialized_intrinsic semantic call helps in
|
|
// stack promotion.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getInitializationCount() const {
|
|
if (getKind() == ArrayCallKind::kArrayUninitialized) {
|
|
// Can be either a call to _adoptStorage or _allocateUninitialized.
|
|
// A call to _adoptStorage has the buffer as AnyObject as the first
|
|
// argument. The count is the second argument.
|
|
// A call to _allocateUninitialized has the count as first argument.
|
|
SILValue Arg0 = SemanticsCall->getArgument(0);
|
|
if (Arg0->getType().isExistentialType() ||
|
|
Arg0->getType().hasReferenceSemantics())
|
|
return SemanticsCall->getArgument(1);
|
|
else return SemanticsCall->getArgument(0);
|
|
}
|
|
|
|
if (getKind() == ArrayCallKind::kArrayInit &&
|
|
SemanticsCall->getNumArguments() == 3)
|
|
// Repeated-value array initializer. Arguments are the value to
|
|
// repeat, the count, and the value's type.
|
|
return SemanticsCall->getArgument(1);
|
|
|
|
return SILValue();
|
|
}
|
|
|
|
/// Given an array semantic call \c arrayCall, if it is an "array.uninitialized"
|
|
/// initializer, which returns a two-element tuple, return the element of the
|
|
/// tuple at \c tupleElementIndex. Return a null SILValue if the
|
|
/// array call is not an "array.uninitialized" initializer or if the extraction
|
|
/// of the result tuple fails.
|
|
static SILValue getArrayUninitializedInitResult(ArraySemanticsCall arrayCall,
|
|
unsigned tupleElementIndex) {
|
|
assert(tupleElementIndex <= 1 && "tupleElementIndex must be 0 or 1");
|
|
ArrayCallKind arrayCallKind = arrayCall.getKind();
|
|
if (arrayCallKind != ArrayCallKind::kArrayUninitialized &&
|
|
arrayCallKind != ArrayCallKind::kArrayUninitializedIntrinsic)
|
|
return SILValue();
|
|
|
|
// In OSSA, the call result will be extracted through a destructure_tuple
|
|
// instruction.
|
|
ApplyInst *callInst = arrayCall;
|
|
if (callInst->getFunction()->hasOwnership()) {
|
|
Operand *singleUse = callInst->getSingleUse();
|
|
if (!singleUse)
|
|
return SILValue();
|
|
if (DestructureTupleInst *destructTuple =
|
|
dyn_cast<DestructureTupleInst>(singleUse->getUser())) {
|
|
return destructTuple->getResult(tupleElementIndex);
|
|
}
|
|
return SILValue();
|
|
}
|
|
|
|
// In non-OSSA, look for a tuple_extract instruction of the call result with
|
|
// the requested tupleElementIndex.
|
|
TupleExtractInst *tupleExtractInst = nullptr;
|
|
for (auto *op : callInst->getUses()) {
|
|
auto *tupleElt = dyn_cast<TupleExtractInst>(op->getUser());
|
|
if (!tupleElt)
|
|
return SILValue();
|
|
if (tupleElt->getFieldIndex() != tupleElementIndex)
|
|
continue;
|
|
tupleExtractInst = tupleElt;
|
|
break;
|
|
}
|
|
return SILValue(tupleExtractInst);
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getArrayValue() const {
|
|
ArrayCallKind arrayCallKind = getKind();
|
|
if (arrayCallKind == ArrayCallKind::kArrayInit
|
|
|| arrayCallKind == ArrayCallKind::kArrayInitEmpty) {
|
|
return SILValue(SemanticsCall);
|
|
}
|
|
return getArrayUninitializedInitResult(*this, 0);
|
|
}
|
|
|
|
SILValue swift::ArraySemanticsCall::getArrayElementStoragePointer() const {
|
|
return getArrayUninitializedInitResult(*this, 1);
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::replaceByValue(SILValue V) {
|
|
assert(getKind() == ArrayCallKind::kGetElement &&
|
|
"Must be a get_element call");
|
|
// We only handle loadable types.
|
|
if (!V->getType().isLoadable(*SemanticsCall->getFunction()))
|
|
return false;
|
|
|
|
if (!hasGetElementDirectResult())
|
|
return false;
|
|
|
|
// Expect a check_subscript call or the empty dependence.
|
|
auto SubscriptCheck = getSubscriptCheckArgument();
|
|
ArraySemanticsCall Check(SubscriptCheck, "array.check_subscript");
|
|
auto *EmptyDep = dyn_cast<StructInst>(SubscriptCheck);
|
|
if (!Check && (!EmptyDep || !EmptyDep->getElements().empty()))
|
|
return false;
|
|
|
|
// In OSSA, the InsertPt is after V's definition and not before SemanticsCall
|
|
// Because we are creating copy_value in ossa, and the source may have been
|
|
// taken previously. So our insert point for copy_value is immediately after
|
|
// V, where we can be sure it is live.
|
|
auto InsertPt = V->getFunction()->hasOwnership()
|
|
? getInsertAfterPoint(V)
|
|
: SemanticsCall->getIterator();
|
|
assert(InsertPt.hasValue());
|
|
|
|
SILValue CopiedVal = SILBuilderWithScope(InsertPt.getValue())
|
|
.emitCopyValueOperation(SemanticsCall->getLoc(), V);
|
|
SemanticsCall->replaceAllUsesWith(CopiedVal);
|
|
|
|
removeCall();
|
|
return true;
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::replaceByAppendingValues(
|
|
SILFunction *AppendFn, SILFunction *ReserveFn,
|
|
const SmallVectorImpl<SILValue> &Vals, SubstitutionMap Subs) {
|
|
assert(getKind() == ArrayCallKind::kAppendContentsOf &&
|
|
"Must be an append_contentsOf call");
|
|
assert(AppendFn && "Must provide an append SILFunction");
|
|
|
|
auto *F = SemanticsCall->getFunction();
|
|
|
|
// We only handle loadable types.
|
|
if (any_of(Vals, [F](SILValue V) -> bool {
|
|
return !V->getType().isLoadable(*F);
|
|
}))
|
|
return false;
|
|
|
|
CanSILFunctionType AppendFnTy = AppendFn->getLoweredFunctionType();
|
|
SILValue ArrRef = SemanticsCall->getArgument(1);
|
|
SILBuilderWithScope Builder(SemanticsCall);
|
|
auto Loc = SemanticsCall->getLoc();
|
|
auto *FnRef = Builder.createFunctionRefFor(Loc, AppendFn);
|
|
|
|
if (Vals.size() > 1) {
|
|
// Create a call to reserveCapacityForAppend() to reserve space for multiple
|
|
// elements.
|
|
FunctionRefBaseInst *ReserveFnRef =
|
|
Builder.createFunctionRefFor(Loc, ReserveFn);
|
|
SILFunctionType *ReserveFnTy =
|
|
ReserveFnRef->getType().castTo<SILFunctionType>();
|
|
assert(ReserveFnTy->getNumParameters() == 2);
|
|
StructType *IntType =
|
|
ReserveFnTy->getParameters()[0]
|
|
.getArgumentType(F->getModule(), ReserveFnTy,
|
|
Builder.getTypeExpansionContext())
|
|
->castTo<StructType>();
|
|
StructDecl *IntDecl = IntType->getDecl();
|
|
VarDecl *field = IntDecl->getStoredProperties()[0];
|
|
SILType BuiltinIntTy =SILType::getPrimitiveObjectType(
|
|
field->getInterfaceType()->getCanonicalType());
|
|
IntegerLiteralInst *CapacityLiteral =
|
|
Builder.createIntegerLiteral(Loc, BuiltinIntTy, Vals.size());
|
|
StructInst *Capacity = Builder.createStruct(Loc,
|
|
SILType::getPrimitiveObjectType(CanType(IntType)), {CapacityLiteral});
|
|
Builder.createApply(Loc, ReserveFnRef, Subs, {Capacity, ArrRef});
|
|
}
|
|
|
|
for (SILValue V : Vals) {
|
|
auto SubTy = V->getType();
|
|
auto &ValLowering = Builder.getTypeLowering(SubTy);
|
|
// In OSSA, the InsertPt is after V's definition and not before
|
|
// SemanticsCall. Because we are creating copy_value in ossa, and the source
|
|
// may have been taken previously. So our insert point for copy_value is
|
|
// immediately after V, where we can be sure it is live.
|
|
auto InsertPt = F->hasOwnership() ? getInsertAfterPoint(V)
|
|
: SemanticsCall->getIterator();
|
|
assert(InsertPt.hasValue());
|
|
SILValue CopiedVal = SILBuilderWithScope(InsertPt.getValue())
|
|
.emitCopyValueOperation(V.getLoc(), V);
|
|
auto *AllocStackInst = Builder.createAllocStack(Loc, SubTy);
|
|
ValLowering.emitStoreOfCopy(Builder, Loc, CopiedVal, AllocStackInst,
|
|
IsInitialization_t::IsInitialization);
|
|
|
|
SILValue Args[] = {AllocStackInst, ArrRef};
|
|
Builder.createApply(Loc, FnRef, Subs, Args);
|
|
Builder.createDeallocStack(Loc, AllocStackInst);
|
|
if (!isConsumedParameter(AppendFnTy->getParameters()[0].getConvention())) {
|
|
ValLowering.emitDestroyValue(Builder, Loc, CopiedVal);
|
|
}
|
|
}
|
|
CanSILFunctionType AppendContentsOfFnTy =
|
|
SemanticsCall->getReferencedFunctionOrNull()->getLoweredFunctionType();
|
|
if (AppendContentsOfFnTy->getParameters()[0].getConvention() ==
|
|
ParameterConvention::Direct_Owned) {
|
|
SILValue SrcArray = SemanticsCall->getArgument(0);
|
|
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), SrcArray);
|
|
}
|
|
|
|
removeCall();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool swift::ArraySemanticsCall::mapInitializationStores(
|
|
llvm::DenseMap<uint64_t, StoreInst *> &ElementValueMap) {
|
|
if (getKind() != ArrayCallKind::kArrayUninitialized &&
|
|
getKind() != ArrayCallKind::kArrayUninitializedIntrinsic)
|
|
return false;
|
|
SILValue ElementBuffer = getArrayElementStoragePointer();
|
|
if (!ElementBuffer)
|
|
return false;
|
|
|
|
// Match initialization stores into ElementBuffer. E.g.
|
|
// %83 = struct_extract %element_buffer : $UnsafeMutablePointer<Int>
|
|
// %84 = pointer_to_address %83 : $Builtin.RawPointer to strict $*Int
|
|
// store %85 to %84 : $*Int
|
|
// %87 = integer_literal $Builtin.Word, 1
|
|
// %88 = index_addr %84 : $*Int, %87 : $Builtin.Word
|
|
// store %some_value to %88 : $*Int
|
|
|
|
// If this an ArrayUinitializedIntrinsic then the ElementBuffer is a
|
|
// builtin.RawPointer. Otherwise, it is an UnsafeMutablePointer, which would
|
|
// be struct-extracted to obtain a builtin.RawPointer.
|
|
SILValue UnsafeMutablePointerExtract =
|
|
(getKind() == ArrayCallKind::kArrayUninitialized)
|
|
? dyn_cast_or_null<StructExtractInst>(
|
|
getSingleNonDebugUser(ElementBuffer))
|
|
: ElementBuffer;
|
|
if (!UnsafeMutablePointerExtract)
|
|
return false;
|
|
|
|
auto *PointerToAddress = dyn_cast_or_null<PointerToAddressInst>(
|
|
getSingleNonDebugUser(UnsafeMutablePointerExtract));
|
|
if (!PointerToAddress)
|
|
return false;
|
|
|
|
// Match the stores. We can have either a store directly to the address or
|
|
// to an index_addr projection.
|
|
for (auto *Op : PointerToAddress->getUses()) {
|
|
auto *Inst = Op->getUser();
|
|
|
|
// Store to the base.
|
|
auto *SI = dyn_cast<StoreInst>(Inst);
|
|
if (SI && SI->getDest() == PointerToAddress) {
|
|
// We have already seen an entry for this index bail.
|
|
if (ElementValueMap.count(0))
|
|
return false;
|
|
ElementValueMap[0] = SI;
|
|
continue;
|
|
} else if (SI)
|
|
return false;
|
|
|
|
// Store to an index_addr projection.
|
|
auto *IndexAddr = dyn_cast<IndexAddrInst>(Inst);
|
|
if (!IndexAddr)
|
|
return false;
|
|
SI = dyn_cast_or_null<StoreInst>(getSingleNonDebugUser(IndexAddr));
|
|
if (!SI || SI->getDest() != IndexAddr)
|
|
return false;
|
|
auto *Index = dyn_cast<IntegerLiteralInst>(IndexAddr->getIndex());
|
|
if (!Index)
|
|
return false;
|
|
auto IndexVal = Index->getValue();
|
|
// Let's not blow up our map.
|
|
if (IndexVal.getActiveBits() > 16)
|
|
return false;
|
|
// Already saw an entry.
|
|
if (ElementValueMap.count(IndexVal.getZExtValue()))
|
|
return false;
|
|
|
|
ElementValueMap[IndexVal.getZExtValue()] = SI;
|
|
}
|
|
return !ElementValueMap.empty();
|
|
}
|