Skip to content

Commit 5013a02

Browse files
authored
Merge pull request #32023 from eeckstein/cow-optimizer-changes
SIL optimizer: a collection of optimizer changes to prepare for COW representation
2 parents 45a78e4 + 3f42ad7 commit 5013a02

36 files changed

+732
-120
lines changed

include/swift/AST/SemanticAttrs.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ SEMANTICS_ATTR(ARRAY_GET_ELEMENT_ADDRESS, "array.get_element_address")
5151
SEMANTICS_ATTR(ARRAY_INIT, "array.init")
5252
SEMANTICS_ATTR(ARRAY_INIT_EMPTY, "array.init.empty")
5353
SEMANTICS_ATTR(ARRAY_MAKE_MUTABLE, "array.make_mutable")
54+
SEMANTICS_ATTR(ARRAY_END_MUTATION, "array.end_mutation")
5455
SEMANTICS_ATTR(ARRAY_MUTATE_UNKNOWN, "array.mutate_unknown")
5556
SEMANTICS_ATTR(ARRAY_PROPS_IS_NATIVE_TYPE_CHECKED, "array.props.isNativeTypeChecked")
5657
SEMANTICS_ATTR(ARRAY_RESERVE_CAPACITY_FOR_APPEND, "array.reserve_capacity_for_append")
@@ -67,6 +68,8 @@ SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER,
6768
"optimize.sil.specialize.generic.partial.never")
6869
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER,
6970
"optimize.sil.specialize.generic.size.never")
71+
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER,
72+
"optimize.sil.specialize.owned2guarantee.never")
7073

7174
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_INTERPOLATION, "oslog.message.init_interpolation")
7275
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_STRING_LITERAL, "oslog.message.init_stringliteral")

include/swift/SILOptimizer/Analysis/ArraySemantic.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum class ArrayCallKind {
3131
kGetElement,
3232
kGetElementAddress,
3333
kMakeMutable,
34+
kEndMutation,
3435
kMutateUnknown,
3536
kReserveCapacityForAppend,
3637
kWithUnsafeMutableBufferPointer,
@@ -42,7 +43,8 @@ enum class ArrayCallKind {
4243
// before this comment.
4344
kArrayInit,
4445
kArrayUninitialized,
45-
kArrayUninitializedIntrinsic
46+
kArrayUninitializedIntrinsic,
47+
kArrayFinalizeIntrinsic
4648
};
4749

4850
/// Return true is the given function is an array semantics call.
@@ -78,6 +80,8 @@ class ArraySemanticsCall {
7880
ArraySemanticsCall(SILValue V, StringRef semanticName,
7981
bool matchPartialName);
8082

83+
ArraySemanticsCall() : SemanticsCall(nullptr) {}
84+
8185
/// Can we hoist this call.
8286
bool canHoist(SILInstruction *To, DominanceInfo *DT) const;
8387

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ PASS(CopyForwarding, "copy-forwarding",
120120
"Copy Forwarding to Remove Redundant Copies")
121121
PASS(CopyPropagation, "copy-propagation",
122122
"Copy propagation to Remove Redundant SSA Copies")
123+
PASS(COWOpts, "cow-opts",
124+
"Optimize COW operations")
123125
PASS(Differentiation, "differentiation",
124126
"Automatic Differentiation")
125127
PASS(EpilogueARCMatcherDumper, "sil-epilogue-arc-dumper",

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ SILValue swift::stripCastsWithoutMarkDependence(SILValue V) {
128128

129129
auto K = V->getKind();
130130
if (isRCIdentityPreservingCast(K) ||
131-
K == ValueKind::UncheckedTrivialBitCastInst) {
131+
K == ValueKind::UncheckedTrivialBitCastInst ||
132+
K == ValueKind::EndCOWMutationInst) {
132133
V = cast<SingleValueInstruction>(V)->getOperand(0);
133134
continue;
134135
}
@@ -308,7 +309,8 @@ bool swift::onlyAffectsRefCount(SILInstruction *user) {
308309
}
309310

310311
bool swift::mayCheckRefCount(SILInstruction *User) {
311-
return isa<IsUniqueInst>(User) || isa<IsEscapingClosureInst>(User);
312+
return isa<IsUniqueInst>(User) || isa<IsEscapingClosureInst>(User) ||
313+
isa<BeginCOWMutationInst>(User);
312314
}
313315

314316
bool swift::isSanitizerInstrumentation(SILInstruction *Instruction) {

lib/SIL/Utils/Projection.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,13 @@ Optional<ProjectionPath> ProjectionPath::getProjectionPath(SILValue Start,
371371

372372
auto Iter = End;
373373
while (Start != Iter) {
374-
Projection AP(Iter);
375-
if (!AP.isValid())
376-
break;
377-
P.Path.push_back(AP);
374+
// end_cow_mutation is not a projection, but we want to "see through" it.
375+
if (!isa<EndCOWMutationInst>(Iter)) {
376+
Projection AP(Iter);
377+
if (!AP.isValid())
378+
break;
379+
P.Path.push_back(AP);
380+
}
378381
Iter = cast<SingleValueInstruction>(*Iter).getOperand(0);
379382
}
380383

lib/SILOptimizer/Analysis/ArraySemantic.cpp

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ ArrayCallKind swift::getArraySemanticsKind(SILFunction *f) {
3333
.StartsWith("array.init", ArrayCallKind::kArrayInit)
3434
.Case("array.uninitialized", ArrayCallKind::kArrayUninitialized)
3535
.Case("array.uninitialized_intrinsic", ArrayCallKind::kArrayUninitializedIntrinsic)
36+
.Case("array.finalize_intrinsic", ArrayCallKind::kArrayFinalizeIntrinsic)
3637
.Case("array.check_subscript", ArrayCallKind::kCheckSubscript)
3738
.Case("array.check_index", ArrayCallKind::kCheckIndex)
3839
.Case("array.get_count", ArrayCallKind::kGetCount)
3940
.Case("array.get_capacity", ArrayCallKind::kGetCapacity)
4041
.Case("array.get_element", ArrayCallKind::kGetElement)
4142
.Case("array.make_mutable", ArrayCallKind::kMakeMutable)
43+
.Case("array.end_mutation", ArrayCallKind::kEndMutation)
4244
.Case("array.get_element_address",
4345
ArrayCallKind::kGetElementAddress)
4446
.Case("array.mutate_unknown", ArrayCallKind::kMutateUnknown)
@@ -101,10 +103,12 @@ bool swift::ArraySemanticsCall::isValidSignature() {
101103
}
102104
case ArrayCallKind::kCheckSubscript: {
103105
// Int, Bool, Self
104-
if (SemanticsCall->getNumArguments() != 3 ||
105-
!SemanticsCall->getArgument(0)->getType().isTrivial(*F))
106+
unsigned numArgs = SemanticsCall->getNumArguments();
107+
if (numArgs != 2 && numArgs != 3)
108+
return false;
109+
if (!SemanticsCall->getArgument(0)->getType().isTrivial(*F))
106110
return false;
107-
if (!SemanticsCall->getArgument(1)->getType().isTrivial(*F))
111+
if (numArgs == 3 && !SemanticsCall->getArgument(1)->getType().isTrivial(*F))
108112
return false;
109113
auto SelfConvention = FnTy->getSelfParameter().getConvention();
110114
return SelfConvention == ParameterConvention::Direct_Guaranteed ||
@@ -324,27 +328,26 @@ bool swift::ArraySemanticsCall::canHoist(SILInstruction *InsertBefore,
324328
// Not implemented yet.
325329
return false;
326330

327-
case ArrayCallKind::kCheckSubscript: {
328-
auto IsNativeArg = getArrayPropertyIsNativeTypeChecked();
329-
ArraySemanticsCall IsNative(IsNativeArg,
330-
"array.props.isNativeTypeChecked", true);
331-
if (!IsNative) {
332-
// Do we have a constant parameter?
333-
auto *SI = dyn_cast<StructInst>(IsNativeArg);
334-
if (!SI)
335-
return false;
336-
if (!isa<IntegerLiteralInst>(SI->getOperand(0)))
331+
case ArrayCallKind::kCheckSubscript:
332+
if (SILValue IsNativeArg = getArrayPropertyIsNativeTypeChecked()) {
333+
ArraySemanticsCall IsNative(IsNativeArg,
334+
"array.props.isNativeTypeChecked", true);
335+
if (!IsNative) {
336+
// Do we have a constant parameter?
337+
auto *SI = dyn_cast<StructInst>(IsNativeArg);
338+
if (!SI)
339+
return false;
340+
if (!isa<IntegerLiteralInst>(SI->getOperand(0)))
341+
return false;
342+
} else if (!IsNative.canHoist(InsertBefore, DT))
343+
// Otherwise, we must be able to hoist the function call.
337344
return false;
338-
} else if (!IsNative.canHoist(InsertBefore, DT))
339-
// Otherwise, we must be able to hoist the function call.
340-
return false;
341-
345+
}
342346
return canHoistArrayArgument(SemanticsCall, getSelf(), InsertBefore, DT);
343-
}
344347

345-
case ArrayCallKind::kMakeMutable: {
348+
case ArrayCallKind::kMakeMutable:
349+
case ArrayCallKind::kEndMutation:
346350
return canHoistArrayArgument(SemanticsCall, getSelf(), InsertBefore, DT);
347-
}
348351
} // End switch.
349352

350353
return false;
@@ -448,9 +451,8 @@ ApplyInst *swift::ArraySemanticsCall::hoistOrCopy(SILInstruction *InsertBefore,
448451
hoistOrCopySelf(SemanticsCall, InsertBefore, DT, LeaveOriginal);
449452

450453
SILValue NewArrayProps;
451-
if (Kind == ArrayCallKind::kCheckSubscript) {
454+
if (SILValue IsNativeArg = getArrayPropertyIsNativeTypeChecked()) {
452455
// Copy the array.props argument call.
453-
auto IsNativeArg = getArrayPropertyIsNativeTypeChecked();
454456
ArraySemanticsCall IsNative(IsNativeArg,
455457
"array.props.isNativeTypeChecked", true);
456458
if (!IsNative) {
@@ -492,8 +494,8 @@ ApplyInst *swift::ArraySemanticsCall::hoistOrCopy(SILInstruction *InsertBefore,
492494
return Call;
493495
}
494496

495-
case ArrayCallKind::kMakeMutable: {
496-
assert(!LeaveOriginal && "Copying not yet implemented");
497+
case ArrayCallKind::kMakeMutable:
498+
case ArrayCallKind::kEndMutation: {
497499
// Hoist the call.
498500
auto Call = hoistOrCopyCall(SemanticsCall, InsertBefore, LeaveOriginal, DT);
499501
return Call;
@@ -515,14 +517,15 @@ void swift::ArraySemanticsCall::removeCall() {
515517

516518
switch (getKind()) {
517519
default: break;
518-
case ArrayCallKind::kCheckSubscript: {
519-
// Remove all uses with the empty tuple ().
520-
auto EmptyDep = SILBuilderWithScope(SemanticsCall)
521-
.createStruct(SemanticsCall->getLoc(),
522-
SemanticsCall->getType(), {});
523-
SemanticsCall->replaceAllUsesWith(EmptyDep);
524-
}
525-
break;
520+
case ArrayCallKind::kCheckSubscript:
521+
if (!SemanticsCall->getType().isVoid()){
522+
// Remove all uses with the empty tuple ().
523+
auto EmptyDep = SILBuilderWithScope(SemanticsCall)
524+
.createStruct(SemanticsCall->getLoc(),
525+
SemanticsCall->getType(), {});
526+
SemanticsCall->replaceAllUsesWith(EmptyDep);
527+
}
528+
break;
526529
case ArrayCallKind::kGetElement: {
527530
// Remove the matching isNativeTypeChecked and check_subscript call.
528531
ArraySemanticsCall IsNative(getTypeCheckedArgument(),
@@ -552,11 +555,13 @@ SILValue
552555
swift::ArraySemanticsCall::getArrayPropertyIsNativeTypeChecked() const {
553556
switch (getKind()) {
554557
case ArrayCallKind::kCheckSubscript:
555-
return SemanticsCall->getArgument(1);
558+
if (SemanticsCall->getNumArguments() == 3)
559+
return SemanticsCall->getArgument(1);
560+
return SILValue();
556561
case ArrayCallKind::kGetElement:
557562
return getTypeCheckedArgument();
558563
default:
559-
llvm_unreachable("Must have an array.props argument");
564+
return SILValue();
560565
}
561566
}
562567

@@ -569,6 +574,7 @@ bool swift::ArraySemanticsCall::doesNotChangeArray() const {
569574
case ArrayCallKind::kGetCount:
570575
case ArrayCallKind::kGetCapacity:
571576
case ArrayCallKind::kGetElement:
577+
case ArrayCallKind::kEndMutation:
572578
return true;
573579
}
574580
}

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
22852285
case SILInstructionKind::SelectValueInst:
22862286
analyzeSelectInst(cast<SelectValueInst>(I), ConGraph);
22872287
return;
2288+
case SILInstructionKind::EndCOWMutationInst:
22882289
case SILInstructionKind::StructInst:
22892290
case SILInstructionKind::TupleInst:
22902291
case SILInstructionKind::EnumInst: {

lib/SILOptimizer/Analysis/SimplifyInstruction.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ namespace {
6262
SILValue visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI);
6363
SILValue
6464
visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI);
65+
SILValue visitEndCOWMutationInst(EndCOWMutationInst *ECM);
6566
SILValue visitThinFunctionToPointerInst(ThinFunctionToPointerInst *TFTPI);
6667
SILValue visitPointerToThinFunctionInst(PointerToThinFunctionInst *PTTFI);
6768
SILValue visitBeginAccessInst(BeginAccessInst *BAI);
@@ -329,6 +330,21 @@ visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) {
329330
return SILValue();
330331
}
331332

333+
/// If the only use of a cast is a destroy, just destroy the cast operand.
334+
static SILValue simplifyDeadCast(SingleValueInstruction *Cast) {
335+
for (Operand *op : Cast->getUses()) {
336+
switch (op->getUser()->getKind()) {
337+
case SILInstructionKind::DestroyValueInst:
338+
case SILInstructionKind::StrongReleaseInst:
339+
case SILInstructionKind::StrongRetainInst:
340+
break;
341+
default:
342+
return SILValue();
343+
}
344+
}
345+
return Cast->getOperand(0);
346+
}
347+
332348
SILValue
333349
InstSimplifier::
334350
visitUncheckedRefCastInst(UncheckedRefCastInst *OPRI) {
@@ -351,7 +367,8 @@ visitUncheckedRefCastInst(UncheckedRefCastInst *OPRI) {
351367
if (OPRI->getOperand()->getType() == OPRI->getType())
352368
return OPRI->getOperand();
353369

354-
return SILValue();
370+
// (destroy_value (unchecked_ref_cast x)) -> destroy_value x
371+
return simplifyDeadCast(OPRI);
355372
}
356373

357374
SILValue
@@ -375,7 +392,8 @@ SILValue InstSimplifier::visitUpcastInst(UpcastInst *UI) {
375392
if (URCI->getOperand()->getType() == UI->getType())
376393
return URCI->getOperand();
377394

378-
return SILValue();
395+
// (destroy_value (upcast x)) -> destroy_value x
396+
return simplifyDeadCast(UI);
379397
}
380398

381399
#define LOADABLE_REF_STORAGE(Name, ...) \
@@ -410,6 +428,11 @@ visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI) {
410428
return SILValue();
411429
}
412430

431+
SILValue InstSimplifier::visitEndCOWMutationInst(EndCOWMutationInst *ECM) {
432+
// (destroy_value (end_cow_mutation x)) -> destroy_value x
433+
return simplifyDeadCast(ECM);
434+
}
435+
413436
SILValue
414437
InstSimplifier::
415438
visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {

lib/SILOptimizer/FunctionSignatureTransforms/OwnedToGuaranteedTransform.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define DEBUG_TYPE "fso-owned-to-guaranteed-transform"
1414
#include "FunctionSignatureOpts.h"
1515
#include "swift/SIL/DebugUtils.h"
16+
#include "swift/AST/SemanticAttrs.h"
1617
#include "llvm/Support/CommandLine.h"
1718

1819
using namespace swift;
@@ -258,6 +259,9 @@ void FunctionSignatureTransform::OwnedToGuaranteedAddResultRelease(
258259
bool FunctionSignatureTransform::OwnedToGuaranteedAnalyze() {
259260
if (FSODisableOwnedToGuaranteed)
260261
return false;
262+
SILFunction *F = TransformDescriptor.OriginalFunction;
263+
if (F->hasSemanticsAttr(semantics::OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER))
264+
return false;
261265

262266
const bool Result = OwnedToGuaranteedAnalyzeResults();
263267
const bool Params = OwnedToGuaranteedAnalyzeParameters();

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ static bool isNonMutatingArraySemanticCall(SILInstruction *Inst) {
288288
case ArrayCallKind::kGetCapacity:
289289
case ArrayCallKind::kGetElement:
290290
case ArrayCallKind::kGetElementAddress:
291+
case ArrayCallKind::kEndMutation:
291292
return true;
292293
case ArrayCallKind::kMakeMutable:
293294
case ArrayCallKind::kMutateUnknown:
@@ -296,6 +297,7 @@ static bool isNonMutatingArraySemanticCall(SILInstruction *Inst) {
296297
case ArrayCallKind::kArrayInit:
297298
case ArrayCallKind::kArrayUninitialized:
298299
case ArrayCallKind::kArrayUninitializedIntrinsic:
300+
case ArrayCallKind::kArrayFinalizeIntrinsic:
299301
case ArrayCallKind::kAppendContentsOf:
300302
case ArrayCallKind::kAppendElement:
301303
return false;

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ void addFunctionPasses(SILPassPipelinePlan &P,
366366
P.addRedundantLoadElimination();
367367
}
368368

369+
P.addCOWOpts();
369370
P.addPerformanceConstantPropagation();
370371
// Remove redundant arguments right before CSE and DCE, so that CSE and DCE
371372
// can cleanup redundant and dead instructions.
@@ -514,6 +515,7 @@ static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) {
514515
P.addLICM();
515516
// Run loop unrolling after inlining and constant propagation, because loop
516517
// trip counts may have became constant.
518+
P.addLICM();
517519
P.addLoopUnroll();
518520
}
519521

@@ -595,6 +597,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) {
595597
P.addAccessEnforcementReleaseSinking();
596598
P.addAccessEnforcementOpts();
597599
P.addLICM();
600+
P.addCOWOpts();
598601
// Simplify CFG after LICM that creates new exit blocks
599602
P.addSimplifyCFG();
600603
// LICM might have added new merging potential by hoisting

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ class SILCombiner :
183183
SILInstruction *visitPointerToAddressInst(PointerToAddressInst *PTAI);
184184
SILInstruction *visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI);
185185
SILInstruction *visitUncheckedRefCastInst(UncheckedRefCastInst *URCI);
186+
SILInstruction *visitEndCOWMutationInst(EndCOWMutationInst *URCI);
186187
SILInstruction *visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI);
187188
SILInstruction *visitBridgeObjectToRefInst(BridgeObjectToRefInst *BORI);
188189
SILInstruction *visitUnconditionalCheckedCastInst(

lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,26 @@ SILCombiner::visitUncheckedRefCastInst(UncheckedRefCastInst *URCI) {
264264
return nullptr;
265265
}
266266

267+
SILInstruction *SILCombiner::visitEndCOWMutationInst(EndCOWMutationInst *ECM) {
268+
269+
// Remove a cast if it's only used by an end_cow_mutation.
270+
//
271+
// (end_cow_mutation (upcast X)) -> (end_cow_mutation X)
272+
// (end_cow_mutation (unchecked_ref_cast X)) -> (end_cow_mutation X)
273+
SILValue op = ECM->getOperand();
274+
if (!isa<UncheckedRefCastInst>(op) && !isa<UpcastInst>(op))
275+
return nullptr;
276+
if (!op->hasOneUse())
277+
return nullptr;
278+
279+
SingleValueInstruction *refCast = cast<SingleValueInstruction>(op);
280+
auto *newECM = Builder.createEndCOWMutation(ECM->getLoc(),
281+
refCast->getOperand(0));
282+
ECM->replaceAllUsesWith(refCast);
283+
refCast->setOperand(0, newECM);
284+
refCast->moveAfter(newECM);
285+
return eraseInstFromFunction(*ECM);
286+
}
267287

268288
SILInstruction *
269289
SILCombiner::visitBridgeObjectToRefInst(BridgeObjectToRefInst *BORI) {

0 commit comments

Comments
 (0)