From 76eca003bb498efb0221b2bfe9745cd3cdddec77 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 17 Apr 2017 10:57:42 -0700 Subject: [PATCH] ArrayElementValuePropagation: explicitly reserve space for new elements when doing the array-content-of optimization When Array.append(contentOf:) is replaced by individual Array.append(element) calls, an explicit reserveCapacityForAppend is inserted. --- include/swift/AST/ASTContext.h | 4 ++ lib/AST/ASTContext.cpp | 65 +++++++++++++++++++ lib/SILOptimizer/Analysis/ArraySemantic.cpp | 26 +++++++- .../ArrayElementValuePropagation.cpp | 15 ++++- test/SILOptimizer/array_contentof_opt.swift | 3 + 5 files changed, 108 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index ab74de9a506..5917627989b 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -457,6 +457,10 @@ public: /// Retrieve the declaration of Array.append(element:) FuncDecl *getArrayAppendElementDecl() const; + /// Retrieve the declaration of + /// Array.reserveCapacityForAppend(newElementsCount: Int) + FuncDecl *getArrayReserveCapacityDecl() const; + /// Retrieve the declaration of Swift._unimplementedInitializer. FuncDecl *getUnimplementedInitializerDecl(LazyResolver *resolver) const; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6b8c55602ae..6f76ab950bf 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -169,6 +169,9 @@ struct ASTContext::Implementation { /// func append(Element) -> void FuncDecl *ArrayAppendElementDecl = nullptr; + /// func reserveCapacityForAppend(newElementsCount: Int) + FuncDecl *ArrayReserveCapacityDecl = nullptr; + /// func _unimplementedInitializer(className: StaticString). FuncDecl *UnimplementedInitializerDecl = nullptr; @@ -967,6 +970,68 @@ FuncDecl *ASTContext::getArrayAppendElementDecl() const { return nullptr; } +FuncDecl *ASTContext::getArrayReserveCapacityDecl() const { + if (Impl.ArrayReserveCapacityDecl) + return Impl.ArrayReserveCapacityDecl; + + auto ReserveFunctions = getArrayDecl()->lookupDirect( + getIdentifier("reserveCapacityForAppend")); + + for (auto CandidateFn : ReserveFunctions) { + auto FnDecl = dyn_cast(CandidateFn); + auto Attrs = FnDecl->getAttrs(); + for (auto *A : Attrs.getAttributes()) { + if (A->Value != "array.reserve_capacity_for_append") + continue; + + auto ParamLists = FnDecl->getParameterLists(); + if (ParamLists.size() != 2) + return nullptr; + if (ParamLists[0]->size() != 1) + return nullptr; + + InOutType *SelfInOutTy = + ParamLists[0]->get(0)->getInterfaceType()->getAs(); + if (!SelfInOutTy) + return nullptr; + + BoundGenericStructType *SelfGenericStructTy = + SelfInOutTy->getObjectType()->getAs(); + if (!SelfGenericStructTy) + return nullptr; + if (SelfGenericStructTy->getDecl() != getArrayDecl()) + return nullptr; + + if (ParamLists[1]->size() != 1) + return nullptr; + StructType *IntType = + ParamLists[1]->get(0)->getInterfaceType()->getAs(); + if (!IntType) + return nullptr; + + StructDecl *IntDecl = IntType->getDecl(); + auto StoredProperties = IntDecl->getStoredProperties(); + auto FieldIter = StoredProperties.begin(); + if (FieldIter == StoredProperties.end()) + return nullptr; + VarDecl *field = *FieldIter; + if (field->hasClangNode()) + return nullptr; + if (!field->getInterfaceType()->is()) + return nullptr; + if (std::next(FieldIter) != StoredProperties.end()) + return nullptr; + + if (!FnDecl->getResultInterfaceType()->isVoid()) + return nullptr; + + Impl.ArrayReserveCapacityDecl = FnDecl; + return FnDecl; + } + } + return nullptr; +} + FuncDecl * ASTContext::getUnimplementedInitializerDecl(LazyResolver *resolver) const { if (Impl.UnimplementedInitializerDecl) diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index b5077fd4a18..c8e8e5751a4 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -698,8 +698,8 @@ bool swift::ArraySemanticsCall::replaceByValue(SILValue V) { } bool swift::ArraySemanticsCall::replaceByAppendingValues( - SILModule &M, SILFunction *AppendFn, const SmallVectorImpl &Vals, - ArrayRef Subs) { + SILModule &M, SILFunction *AppendFn, SILFunction *ReserveFn, + const SmallVectorImpl &Vals, ArrayRef Subs) { assert(getKind() == ArrayCallKind::kAppendContentsOf && "Must be an append_contentsOf call"); assert(AppendFn && "Must provide an append SILFunction"); @@ -717,6 +717,28 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues( auto *FnRef = Builder.createFunctionRef(Loc, AppendFn); auto FnTy = FnRef->getType(); + if (Vals.size() > 1) { + // Create a call to reserveCapacityForAppend() to reserve apce for multiple + // elements. + FunctionRefInst *ReserveFnRef = Builder.createFunctionRef(Loc, ReserveFn); + SILFunctionType *ReserveFnTy = + ReserveFnRef->getType().castTo(); + assert(ReserveFnTy->getNumParameters() == 2); + StructType *IntType = + ReserveFnTy->getParameters()[0].getType()->castTo(); + StructDecl *IntDecl = IntType->getDecl(); + VarDecl *field = *IntDecl->getStoredProperties().begin(); + 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, FnTy.substGenericArgs(M, Subs), + ReserveFnTy->getAllResultsType(), Subs, + {Capacity, ArrRef}, false); + } + for (SILValue V : Vals) { auto SubTy = V->getType(); auto &ValLowering = Builder.getModule().getTypeLowering(SubTy); diff --git a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp index 5103bd3032c..a175954e920 100644 --- a/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp +++ b/lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp @@ -299,15 +299,24 @@ public: DEBUG(llvm::dbgs() << "Array append contentsOf calls replaced in " << Fn.getName() << " (" << Repls.size() << ")\n"); - auto *AppendFnDecl = Ctx.getArrayAppendElementDecl(); + FuncDecl *AppendFnDecl = Ctx.getArrayAppendElementDecl(); if (!AppendFnDecl) return false; + FuncDecl *ReserveFnDecl = Ctx.getArrayReserveCapacityDecl(); + if (!ReserveFnDecl) + return false; + auto Mangled = SILDeclRef(AppendFnDecl, SILDeclRef::Kind::Func).mangle(); - auto *AppendFn = M.findFunction(Mangled, SILLinkage::PublicExternal); + SILFunction *AppendFn = M.findFunction(Mangled, SILLinkage::PublicExternal); if (!AppendFn) return false; + Mangled = SILDeclRef(ReserveFnDecl, SILDeclRef::Kind::Func).mangle(); + SILFunction *ReserveFn = M.findFunction(Mangled, SILLinkage::PublicExternal); + if (!ReserveFn) + return false; + for (const ArrayAllocation::AppendContentOfReplacement &Repl : Repls) { ArraySemanticsCall AppendContentsOf(Repl.AppendContentOfCall); assert(AppendContentsOf && "Must be AppendContentsOf call"); @@ -322,7 +331,7 @@ public: SmallVector Subs; Sig->getSubstitutions(ArraySubMap, Subs); - AppendContentsOf.replaceByAppendingValues(M, AppendFn, + AppendContentsOf.replaceByAppendingValues(M, AppendFn, ReserveFn, Repl.ReplacementValues, Subs); } return true; diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index c3c2b1986f7..4085dbc7a11 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -16,6 +16,9 @@ public func testInt(_ a: inout [Int]) { // CHECK-LABEL: sil @{{.*}}testThreeInt // CHECK-NOT: apply +// CHECK: [[FR:%[0-9]+]] = function_ref @_T0Sa15reserveCapacityySiFSi_Tg5 +// CHECK-NEXT: apply [[FR]] +// CHECK-NOT: apply // CHECK: [[F:%[0-9]+]] = function_ref @_T0Sa6appendyxFSi_Tg // CHECK-NOT: apply // CHECK: apply [[F]]