Skip to content

Commit 472f442

Browse files
author
Ben Ng
committed
SILOptimizer: Replace [].append(contentsOf:) with [].append(element:)
1 parent 5a6867a commit 472f442

File tree

8 files changed

+292
-33
lines changed

8 files changed

+292
-33
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,10 @@ class ASTContext {
450450

451451
/// Retrieve the declaration of Swift.==(Int, Int) -> Bool.
452452
FuncDecl *getEqualIntDecl() const;
453-
453+
454+
/// Retrieve the declaration of Array.append(element:)
455+
FuncDecl *getArrayAppendElementDecl() const;
456+
454457
/// Retrieve the declaration of Swift._unimplementedInitializer.
455458
FuncDecl *getUnimplementedInitializerDecl(LazyResolver *resolver) const;
456459

include/swift/SILOptimizer/Analysis/ArraySemantic.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ enum class ArrayCallKind {
3434
kMakeMutable,
3535
kMutateUnknown,
3636
kWithUnsafeMutableBufferPointer,
37+
kAppendContentsOf,
38+
kAppendElement,
3739
// The following two semantic function kinds return the result @owned
38-
// instead of operating on self passed as parameter.
40+
// instead of operating on self passed as parameter. If you are adding
41+
// a function, and it has a self parameter, make sure that it is defined
42+
// before this comment.
3943
kArrayInit,
4044
kArrayUninitialized
4145
};
@@ -131,6 +135,12 @@ class ArraySemanticsCall {
131135
/// Returns true on success, false otherwise.
132136
bool replaceByValue(SILValue V);
133137

138+
/// Replace a call to append(contentsOf: ) with a series of
139+
/// append(element: ) calls.
140+
bool replaceByAppendingValues(SILModule &M, SILFunction *AppendFn,
141+
llvm::SmallVectorImpl<SILValue> &Vals,
142+
ArrayRef<Substitution> Subs);
143+
134144
/// Hoist the call to the insert point.
135145
void hoist(SILInstruction *InsertBefore, DominanceInfo *DT) {
136146
hoistOrCopy(InsertBefore, DT, false);

lib/AST/ASTContext.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ struct ASTContext::Implementation {
162162
/// func ==(Int, Int) -> Bool
163163
FuncDecl *EqualIntDecl = nullptr;
164164

165+
/// func append(Element) -> void
166+
FuncDecl *ArrayAppendElementDecl = nullptr;
167+
165168
/// func _unimplementedInitializer(className: StaticString).
166169
FuncDecl *UnimplementedInitializerDecl = nullptr;
167170

@@ -883,6 +886,55 @@ FuncDecl *ASTContext::getEqualIntDecl() const {
883886
return nullptr;
884887
}
885888

889+
FuncDecl *ASTContext::getArrayAppendElementDecl() const {
890+
if (Impl.ArrayAppendElementDecl)
891+
return Impl.ArrayAppendElementDecl;
892+
893+
auto AppendFunctions = getArrayDecl()->lookupDirect(getIdentifier("append"));
894+
895+
for (auto CandidateFn : AppendFunctions) {
896+
auto FnDecl = dyn_cast<FuncDecl>(CandidateFn);
897+
auto Attrs = FnDecl->getAttrs();
898+
for (auto *A : Attrs.getAttributes<SemanticsAttr, false>()) {
899+
if (A->Value != "array.append_element")
900+
continue;
901+
902+
#ifndef NDEBUG
903+
auto ParamLists = FnDecl->getParameterLists();
904+
assert(ParamLists.size() == 2 && "Should have two parameter lists");
905+
assert(ParamLists[0]->size() == 1 &&
906+
"First parameter list should have one parameter");
907+
auto SelfInOutTy = ParamLists[0]->get(0)->getInterfaceType()->getAs<InOutType>();
908+
assert(SelfInOutTy && "Self parameter should be an inout Type");
909+
auto SelfGenericStructTy =
910+
SelfInOutTy->getObjectType()->getAs<BoundGenericStructType>();
911+
assert(SelfGenericStructTy &&
912+
"Self parameter should be a BoundGenericStructType Type");
913+
assert(SelfGenericStructTy->getDecl() == getArrayDecl() &&
914+
"Self parameter should be an Array");
915+
916+
assert(ParamLists[1]->size() == 1 &&
917+
"Second parameter list should have one parameter");
918+
auto ElementType = ParamLists[1]
919+
->get(0)
920+
->getInterfaceType()
921+
->getAs<GenericTypeParamType>();
922+
assert(ElementType &&
923+
"First parameter replacement should be a GenericTypeParamType");
924+
assert(ElementType->getName() == getIdentifier("Element") &&
925+
"The GenericTypeParamType's name should be \"Element\"");
926+
927+
assert(FnDecl->getResultInterfaceType()->isVoid() &&
928+
"The return type should be void");
929+
#endif
930+
Impl.ArrayAppendElementDecl = FnDecl;
931+
return FnDecl;
932+
}
933+
}
934+
935+
return NULL;
936+
}
937+
886938
FuncDecl *
887939
ASTContext::getUnimplementedInitializerDecl(LazyResolver *resolver) const {
888940
if (Impl.UnimplementedInitializerDecl)

lib/SILOptimizer/Analysis/ArraySemantic.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ ArrayCallKind swift::ArraySemanticsCall::getKind() const {
162162
.Case("array.get_element_address",
163163
ArrayCallKind::kGetElementAddress)
164164
.Case("array.mutate_unknown", ArrayCallKind::kMutateUnknown)
165-
.Case("array.withUnsafeMutableBufferPointer", ArrayCallKind::kWithUnsafeMutableBufferPointer)
165+
.Case("array.withUnsafeMutableBufferPointer",
166+
ArrayCallKind::kWithUnsafeMutableBufferPointer)
167+
.Case("array.append_contentsOf", ArrayCallKind::kAppendContentsOf)
168+
.Case("array.append_element", ArrayCallKind::kAppendElement)
166169
.Default(ArrayCallKind::kNone);
167170
if (Tmp != ArrayCallKind::kNone) {
168171
assert(Kind == ArrayCallKind::kNone && "Multiple array semantic "
@@ -681,3 +684,42 @@ bool swift::ArraySemanticsCall::replaceByValue(SILValue V) {
681684
removeCall();
682685
return true;
683686
}
687+
688+
bool swift::ArraySemanticsCall::replaceByAppendingValues(
689+
SILModule &M, SILFunction *AppendFn, SmallVectorImpl<SILValue> &Vals,
690+
ArrayRef<Substitution> Subs) {
691+
assert(getKind() == ArrayCallKind::kAppendContentsOf &&
692+
"Must be an append_contentsOf call");
693+
assert(AppendFn && "Must provide an append SILFunction");
694+
695+
// We only handle loadable types.
696+
if (any_of(Vals, [&M](SILValue V) -> bool {
697+
return !V->getType().isLoadable(M);
698+
}))
699+
return false;
700+
701+
auto ArrRef = SemanticsCall->getArgument(1);
702+
SILBuilderWithScope Builder(SemanticsCall);
703+
auto Loc = SemanticsCall->getLoc();
704+
auto *FnRef = Builder.createFunctionRef(Loc, AppendFn);
705+
auto FnTy = FnRef->getType();
706+
707+
for (auto &V : Vals) {
708+
auto SubTy = V->getType();
709+
auto &ValLowering = Builder.getModule().getTypeLowering(SubTy);
710+
auto CopiedVal = ValLowering.emitCopyValue(Builder, Loc, V);
711+
auto *AllocStackInst = Builder.createAllocStack(Loc, SubTy);
712+
ValLowering.emitStoreOfCopy(Builder, Loc, CopiedVal, AllocStackInst,
713+
IsInitialization_t::IsInitialization);
714+
SILValue Args[] = {AllocStackInst, ArrRef};
715+
Builder.createApply(Loc, FnRef, FnTy.substGenericArgs(M, Subs),
716+
FnTy.castTo<SILFunctionType>()->getAllResultsType(), Subs,
717+
Args, false);
718+
Builder.createDeallocStack(Loc, AllocStackInst);
719+
ValLowering.emitDestroyValue(Builder, Loc, CopiedVal);
720+
}
721+
722+
removeCall();
723+
724+
return true;
725+
}

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ static bool isNonMutatingArraySemanticCall(SILInstruction *Inst) {
489489
case ArrayCallKind::kWithUnsafeMutableBufferPointer:
490490
case ArrayCallKind::kArrayInit:
491491
case ArrayCallKind::kArrayUninitialized:
492+
case ArrayCallKind::kAppendContentsOf:
493+
case ArrayCallKind::kAppendElement:
492494
return false;
493495
}
494496

@@ -825,6 +827,8 @@ static bool mayChangeArrayValueToNonUniqueState(ArraySemanticsCall &Call) {
825827
case ArrayCallKind::kWithUnsafeMutableBufferPointer:
826828
case ArrayCallKind::kArrayInit:
827829
case ArrayCallKind::kArrayUninitialized:
830+
case ArrayCallKind::kAppendContentsOf:
831+
case ArrayCallKind::kAppendElement:
828832
return true;
829833
}
830834

0 commit comments

Comments
 (0)