Skip to content

Commit 291373f

Browse files
authored
Merge pull request #32134 from eeckstein/cow-support2
stdlib, SIL optimizer: use the SIL copy-on-write representation in the Array types.
2 parents 13f6885 + f071136 commit 291373f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1475
-557
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ FUNC_DECL(AllocateUninitializedArray,
4444
"_allocateUninitializedArray")
4545
FUNC_DECL(DeallocateUninitializedArray,
4646
"_deallocateUninitializedArray")
47+
FUNC_DECL(FinalizeUninitializedArray,
48+
"_finalizeUninitializedArray")
4749

4850
FUNC_DECL(ForceBridgeFromObjectiveC,
4951
"_forceBridgeFromObjectiveC")

include/swift/AST/SemanticAttrs.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ SEMANTICS_ATTR(ARRAY_WITH_UNSAFE_MUTABLE_BUFFER_POINTER, "array.withUnsafeMutabl
6060
SEMANTICS_ATTR(ARRAY_COUNT, "array.count")
6161
SEMANTICS_ATTR(ARRAY_DEALLOC_UNINITIALIZED, "array.dealloc_uninitialized")
6262
SEMANTICS_ATTR(ARRAY_UNINITIALIZED_INTRINSIC, "array.uninitialized_intrinsic")
63+
SEMANTICS_ATTR(ARRAY_FINALIZE_INTRINSIC, "array.finalize_intrinsic")
6364

6465
SEMANTICS_ATTR(SEQUENCE_FOR_EACH, "sequence.forEach")
6566

include/swift/AST/SemanticAttrs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_SEMANTICS_H
2020
#define SWIFT_SEMANTICS_H
2121

22+
#include "swift/Basic/LLVM.h"
2223
#include "llvm/ADT/StringRef.h"
2324

2425
namespace swift {

include/swift/SIL/SILNodes.def

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,13 @@ NODE_RANGE(NonValueInstruction, UnreachableInst, CondFailInst)
880880
ABSTRACT_INST(MultipleValueInstruction, SILInstruction)
881881
FULLAPPLYSITE_MULTIPLE_VALUE_INST(BeginApplyInst, begin_apply,
882882
MultipleValueInstruction, MayHaveSideEffects, MayRelease)
883+
884+
// begin_cow_mutation is defined to have side effects, because it has
885+
// dependencies with instructions which retain the buffer operand. This prevents
886+
// optimizations from moving begin_cow_mutation instructions across such retain
887+
// instructions.
883888
MULTIPLE_VALUE_INST(BeginCOWMutationInst, begin_cow_mutation,
884-
MultipleValueInstruction, None, DoesNotRelease)
889+
MultipleValueInstruction, MayHaveSideEffects, DoesNotRelease)
885890
MULTIPLE_VALUE_INST(DestructureStructInst, destructure_struct,
886891
MultipleValueInstruction, None, DoesNotRelease)
887892
MULTIPLE_VALUE_INST(DestructureTupleInst, destructure_tuple,

include/swift/SILOptimizer/Differentiation/Common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
#ifndef SWIFT_SILOPTIMIZER_UTILS_DIFFERENTIATION_COMMON_H
1818
#define SWIFT_SILOPTIMIZER_UTILS_DIFFERENTIATION_COMMON_H
1919

20+
#include "swift/AST/SemanticAttrs.h"
2021
#include "swift/SIL/SILDifferentiabilityWitness.h"
2122
#include "swift/SIL/SILFunction.h"
2223
#include "swift/SIL/SILModule.h"
2324
#include "swift/SIL/TypeSubstCloner.h"
25+
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
2426
#include "swift/SILOptimizer/Analysis/DifferentiableActivityAnalysis.h"
2527

2628
namespace swift {

lib/SIL/IR/SILModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ bool SILModule::linkFunction(SILFunction *F, SILModule::LinkingMode Mode) {
355355

356356
SILFunction *SILModule::findFunction(StringRef Name, SILLinkage Linkage) {
357357
assert((Linkage == SILLinkage::Public ||
358+
Linkage == SILLinkage::SharedExternal ||
358359
Linkage == SILLinkage::PublicExternal) &&
359360
"Only a lookup of public functions is supported currently");
360361

@@ -405,6 +406,9 @@ SILFunction *SILModule::findFunction(StringRef Name, SILLinkage Linkage) {
405406
// compilation, simply convert it into an external declaration,
406407
// so that a compiled version from the shared library is used.
407408
if (F->isDefinition() &&
409+
// Don't eliminate bodies of _alwaysEmitIntoClient functions
410+
// (PublicNonABI linkage is de-serialized as SharedExternal)
411+
F->getLinkage() != SILLinkage::SharedExternal &&
408412
!F->getModule().getOptions().shouldOptimize()) {
409413
F->convertToDeclaration();
410414
}

lib/SILGen/SILGenApply.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4962,6 +4962,22 @@ void SILGenFunction::emitUninitializedArrayDeallocation(SILLocation loc,
49624962
SGFContext());
49634963
}
49644964

4965+
ManagedValue SILGenFunction::emitUninitializedArrayFinalization(SILLocation loc,
4966+
SILValue array) {
4967+
auto &Ctx = getASTContext();
4968+
auto finalize = Ctx.getFinalizeUninitializedArray();
4969+
4970+
CanType arrayTy = array->getType().getASTType();
4971+
4972+
// Invoke the intrinsic.
4973+
auto subMap = arrayTy->getContextSubstitutionMap(SGM.M.getSwiftModule(),
4974+
Ctx.getArrayDecl());
4975+
RValue result = emitApplyOfLibraryIntrinsic(loc, finalize, subMap,
4976+
ManagedValue::forUnmanaged(array),
4977+
SGFContext());
4978+
return std::move(result).getScalarValue();
4979+
}
4980+
49654981
namespace {
49664982
/// A cleanup that deallocates an uninitialized array.
49674983
class DeallocateUninitializedArray: public Cleanup {

lib/SILGen/SILGenExpr.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,10 +2113,11 @@ ManagedValue Lowering::emitEndVarargs(SILGenFunction &SGF, SILLocation loc,
21132113
SGF.Cleanups.setCleanupState(varargs.getAbortCleanup(), CleanupState::Dead);
21142114

21152115
// Reactivate the result cleanup.
2116-
auto result = varargs.getArray();
2117-
if (result.hasCleanup())
2118-
SGF.Cleanups.setCleanupState(result.getCleanup(), CleanupState::Active);
2119-
return result;
2116+
auto array = varargs.getArray();
2117+
if (array.hasCleanup())
2118+
SGF.Cleanups.setCleanupState(array.getCleanup(), CleanupState::Active);
2119+
2120+
return SGF.emitUninitializedArrayFinalization(loc, array.forward(SGF));
21202121
}
21212122

21222123
RValue RValueEmitter::visitTupleExpr(TupleExpr *E, SGFContext C) {

lib/SILGen/SILGenFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
11871187

11881188
CleanupHandle enterDeallocateUninitializedArrayCleanup(SILValue array);
11891189
void emitUninitializedArrayDeallocation(SILLocation loc, SILValue array);
1190+
ManagedValue emitUninitializedArrayFinalization(SILLocation loc, SILValue array);
11901191

11911192
/// Emit a cleanup for an owned value that should be written back at end of
11921193
/// scope if the value is not forwarded.

lib/SILOptimizer/Analysis/MemoryBehavior.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class MemoryBehaviorVisitor
180180
MemBehavior visitStrongReleaseInst(StrongReleaseInst *BI);
181181
MemBehavior visitReleaseValueInst(ReleaseValueInst *BI);
182182
MemBehavior visitSetDeallocatingInst(SetDeallocatingInst *BI);
183+
MemBehavior visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI);
183184
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
184185
MemBehavior visit##Name##ReleaseInst(Name##ReleaseInst *BI);
185186
#include "swift/AST/ReferenceStorage.def"
@@ -395,6 +396,14 @@ MemBehavior MemoryBehaviorVisitor::visitSetDeallocatingInst(SetDeallocatingInst
395396
return MemBehavior::None;
396397
}
397398

399+
MemBehavior MemoryBehaviorVisitor::
400+
visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) {
401+
// begin_cow_mutation is defined to have side effects, because it has
402+
// dependencies with instructions which retain the buffer operand.
403+
// But it never interferes with any memory address.
404+
return MemBehavior::None;
405+
}
406+
398407
//===----------------------------------------------------------------------===//
399408
// Top Level Entrypoint
400409
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Differentiation/LinearMapInfo.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ void LinearMapInfo::generateDifferentiationDataStructures(
414414
// initialization is linear and handled separately.
415415
if (!shouldDifferentiateApplySite(ai) || isArrayLiteralIntrinsic(ai))
416416
continue;
417+
if (ArraySemanticsCall(ai, semantics::ARRAY_FINALIZE_INTRINSIC))
418+
continue;
417419
LLVM_DEBUG(getADDebugStream()
418420
<< "Adding linear map struct field for " << *ai);
419421
addLinearMapToStruct(context, ai);

lib/SILOptimizer/Differentiation/PullbackEmitter.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,19 @@ void PullbackEmitter::visitApplyInst(ApplyInst *ai) {
14241424
// special `store` and `copy_addr` support.
14251425
if (isArrayLiteralIntrinsic(ai))
14261426
return;
1427+
auto loc = ai->getLoc();
1428+
auto *bb = ai->getParent();
1429+
// Handle `array.finalize_intrinsic` applications. `array.finalize_intrinsic`
1430+
// semantically behaves like an identity function.
1431+
if (ArraySemanticsCall(ai, semantics::ARRAY_FINALIZE_INTRINSIC)) {
1432+
assert(ai->getNumArguments() == 1 &&
1433+
"Expected intrinsic to have one operand");
1434+
// Accumulate result's adjoint into argument's adjoint.
1435+
auto adjResult = getAdjointValue(bb, ai);
1436+
auto origArg = ai->getArgumentsWithoutIndirectResults().front();
1437+
addAdjointValue(bb, origArg, adjResult, loc);
1438+
return;
1439+
}
14271440
// Replace a call to a function with a call to its pullback.
14281441
auto &nestedApplyInfo = getContext().getNestedApplyInfo();
14291442
auto applyInfoLookup = nestedApplyInfo.find(ai);
@@ -1439,7 +1452,6 @@ void PullbackEmitter::visitApplyInst(ApplyInst *ai) {
14391452
// Get the pullback.
14401453
auto *field = getPullbackInfo().lookUpLinearMapDecl(ai);
14411454
assert(field);
1442-
auto loc = ai->getLoc();
14431455
auto pullback = getPullbackStructElement(ai->getParent(), field);
14441456

14451457
// Get the original result of the `apply` instruction.
@@ -1478,7 +1490,6 @@ void PullbackEmitter::visitApplyInst(ApplyInst *ai) {
14781490
}
14791491

14801492
// Get formal callee pullback arguments.
1481-
auto *bb = ai->getParent();
14821493
assert(applyInfo.indices.results->getNumIndices() == 1);
14831494
for (auto resultIndex : applyInfo.indices.results->getIndices()) {
14841495
assert(resultIndex < origAllResults.size());

lib/SILOptimizer/Differentiation/VJPEmitter.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,15 @@ void VJPEmitter::visitApplyInst(ApplyInst *ai) {
541541
TypeSubstCloner::visitApplyInst(ai);
542542
return;
543543
}
544+
// If callee is `array.finalize_intrinsic`, do standard cloning.
545+
// `array.finalize_intrinsic` has special-case pullback generation.
546+
if (ArraySemanticsCall(ai, semantics::ARRAY_FINALIZE_INTRINSIC)) {
547+
LLVM_DEBUG(getADDebugStream()
548+
<< "Cloning `array.finalize_intrinsic` `apply`:\n"
549+
<< *ai << '\n');
550+
TypeSubstCloner::visitApplyInst(ai);
551+
return;
552+
}
544553
// If the original function is a semantic member accessor, do standard
545554
// cloning. Semantic member accessors have special pullback generation logic,
546555
// so all `apply` instructions can be directly cloned to the VJP.

lib/SILOptimizer/LoopTransforms/ArrayOpt.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,17 @@ class StructUseCollector {
118118
}
119119
}
120120

121-
/// Returns true if there is a single address user of the value.
122-
bool hasSingleAddressUse(SILInstruction *SingleAddressUser) {
121+
/// Returns true if there are only address users of the value.
122+
bool hasOnlyAddressUses(ApplyInst *use1, ApplyInst *use2) {
123123
if (!AggregateAddressUsers.empty())
124124
return false;
125125
if (!ElementAddressUsers.empty())
126126
return false;
127-
if (StructAddressUsers.size() != 1)
128-
return false;
129-
return StructAddressUsers[0] == SingleAddressUser;
127+
for (SILInstruction *user : StructAddressUsers) {
128+
if (user != use1 && user != use2)
129+
return false;
130+
}
131+
return true;
130132
}
131133

132134
protected:

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,18 @@ bool COWArrayOpt::checkSafeArrayAddressUses(UserList &AddressUsers) {
436436
return true;
437437
}
438438

439+
template <typename UserRange>
440+
ArraySemanticsCall getEndMutationCall(const UserRange &AddressUsers) {
441+
for (auto *UseInst : AddressUsers) {
442+
if (auto *AI = dyn_cast<ApplyInst>(UseInst)) {
443+
ArraySemanticsCall ASC(AI);
444+
if (ASC.getKind() == ArrayCallKind::kEndMutation)
445+
return ASC;
446+
}
447+
}
448+
return ArraySemanticsCall();
449+
}
450+
439451
/// Returns true if this instruction is a safe array use if all of its users are
440452
/// also safe array users.
441453
static SILValue isTransitiveSafeUser(SILInstruction *I) {
@@ -642,7 +654,7 @@ bool COWArrayOpt::hasLoopOnlyDestructorSafeArrayOperations() {
642654

643655
// Semantic calls are safe.
644656
ArraySemanticsCall Sem(Inst);
645-
if (Sem) {
657+
if (Sem && Sem.hasSelf()) {
646658
auto Kind = Sem.getKind();
647659
// Safe because they create new arrays.
648660
if (Kind == ArrayCallKind::kArrayInit ||
@@ -811,8 +823,14 @@ void COWArrayOpt::hoistAddressProjections(Operand &ArrayOp) {
811823
}
812824
}
813825

814-
/// Check if this call to "make_mutable" is hoistable, and move it, or delete it
815-
/// if it's already hoisted.
826+
/// Check if this call to "make_mutable" is hoistable, and copy it, along with
827+
/// the corresponding end_mutation call, to the loop pre-header.
828+
///
829+
/// The origial make_mutable/end_mutation calls remain in the loop, because
830+
/// removing them would violate the COW representation rules.
831+
/// Having those calls in the pre-header will then enable COWOpts (after
832+
/// inlining) to constant fold the uniqueness check of the begin_cow_mutation
833+
/// in the loop.
816834
bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable,
817835
bool dominatesExits) {
818836
LLVM_DEBUG(llvm::dbgs() << " Checking mutable array: " <<CurrentArrayAddr);
@@ -872,6 +890,18 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable,
872890
return false;
873891
}
874892

893+
auto ArrayUsers = llvm::map_range(MakeMutable.getSelf()->getUses(),
894+
ValueBase::UseToUser());
895+
896+
// There should be a call to end_mutation. Find it so that we can copy it to
897+
// the pre-header.
898+
ArraySemanticsCall EndMutation = getEndMutationCall(ArrayUsers);
899+
if (!EndMutation) {
900+
EndMutation = getEndMutationCall(StructUses.StructAddressUsers);
901+
if (!EndMutation)
902+
return false;
903+
}
904+
875905
// Hoist the make_mutable.
876906
LLVM_DEBUG(llvm::dbgs() << " Hoisting make_mutable: " << *MakeMutable);
877907

@@ -880,12 +910,18 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable,
880910
assert(MakeMutable.canHoist(Preheader->getTerminator(), DomTree) &&
881911
"Should be able to hoist make_mutable");
882912

883-
MakeMutable.hoist(Preheader->getTerminator(), DomTree);
913+
// Copy the make_mutable and end_mutation calls to the pre-header.
914+
TermInst *insertionPoint = Preheader->getTerminator();
915+
ApplyInst *hoistedMM = MakeMutable.copyTo(insertionPoint, DomTree);
916+
ApplyInst *EMInst = EndMutation;
917+
ApplyInst *hoistedEM = cast<ApplyInst>(EMInst->clone(insertionPoint));
918+
hoistedEM->setArgument(0, hoistedMM->getArgument(0));
919+
placeFuncRef(hoistedEM, DomTree);
884920

885921
// Register array loads. This is needed for hoisting make_mutable calls of
886922
// inner arrays in the two-dimensional case.
887923
if (arrayContainerIsUnique &&
888-
StructUses.hasSingleAddressUse((ApplyInst *)MakeMutable)) {
924+
StructUses.hasOnlyAddressUses((ApplyInst *)MakeMutable, EMInst)) {
889925
for (auto use : MakeMutable.getSelf()->getUses()) {
890926
if (auto *LI = dyn_cast<LoadInst>(use->getUser()))
891927
HoistableLoads.insert(LI);
@@ -917,39 +953,33 @@ bool COWArrayOpt::run() {
917953
// is only mapped to a call once the analysis has determined that no
918954
// make_mutable calls are required within the loop body for that array.
919955
llvm::SmallDenseMap<SILValue, ApplyInst*> ArrayMakeMutableMap;
920-
956+
957+
llvm::SmallVector<ArraySemanticsCall, 8> makeMutableCalls;
958+
921959
for (auto *BB : Loop->getBlocks()) {
922960
if (ColdBlocks.isCold(BB))
923961
continue;
924-
bool dominatesExits = dominatesExitingBlocks(BB);
925-
for (auto II = BB->begin(), IE = BB->end(); II != IE;) {
926-
// Inst may be moved by hoistMakeMutable.
927-
SILInstruction *Inst = &*II;
928-
++II;
929-
ArraySemanticsCall MakeMutableCall(Inst, "array.make_mutable");
930-
if (!MakeMutableCall)
931-
continue;
962+
963+
// Instructions are getting moved around. To not mess with iterator
964+
// invalidation, first collect all calls, and then do the transformation.
965+
for (SILInstruction &I : *BB) {
966+
ArraySemanticsCall MakeMutableCall(&I, "array.make_mutable");
967+
if (MakeMutableCall)
968+
makeMutableCalls.push_back(MakeMutableCall);
969+
}
932970

971+
bool dominatesExits = dominatesExitingBlocks(BB);
972+
for (ArraySemanticsCall MakeMutableCall : makeMutableCalls) {
933973
CurrentArrayAddr = MakeMutableCall.getSelf();
934974
auto HoistedCallEntry = ArrayMakeMutableMap.find(CurrentArrayAddr);
935975
if (HoistedCallEntry == ArrayMakeMutableMap.end()) {
936-
if (!hoistMakeMutable(MakeMutableCall, dominatesExits)) {
976+
if (hoistMakeMutable(MakeMutableCall, dominatesExits)) {
977+
ArrayMakeMutableMap[CurrentArrayAddr] = MakeMutableCall;
978+
HasChanged = true;
979+
} else {
937980
ArrayMakeMutableMap[CurrentArrayAddr] = nullptr;
938-
continue;
939981
}
940-
941-
ArrayMakeMutableMap[CurrentArrayAddr] = MakeMutableCall;
942-
HasChanged = true;
943-
continue;
944982
}
945-
946-
if (!HoistedCallEntry->second)
947-
continue;
948-
949-
LLVM_DEBUG(llvm::dbgs() << " Removing make_mutable call: "
950-
<< *MakeMutableCall);
951-
MakeMutableCall.removeCall();
952-
HasChanged = true;
953983
}
954984
}
955985
return HasChanged;

lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,15 @@ void ArrayInfo::classifyUsesOfArray(SILValue arrayValue) {
323323
// as the array itself is not modified (which is possible with reference
324324
// types).
325325
ArraySemanticsCall arrayOp(user);
326-
if (!arrayOp.doesNotChangeArray())
327-
mayBeWritten = true;
326+
if (arrayOp.doesNotChangeArray())
327+
continue;
328+
329+
if (arrayOp.getKind() == swift::ArrayCallKind::kArrayFinalizeIntrinsic) {
330+
classifyUsesOfArray((ApplyInst *)arrayOp);
331+
continue;
332+
}
333+
334+
mayBeWritten = true;
328335
}
329336
}
330337

0 commit comments

Comments
 (0)