Skip to content

Commit 00ea81c

Browse files
committed
Enable ArrayBoundsCheckElimination on OSSA
1 parent a5803f0 commit 00ea81c

File tree

5 files changed

+2874
-42
lines changed

5 files changed

+2874
-42
lines changed

lib/SILOptimizer/Analysis/ArraySemantic.cpp

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
1414
#include "swift/SIL/DebugUtils.h"
15+
#include "swift/SIL/InstructionUtils.h"
1516
#include "swift/SIL/SILArgument.h"
1617
#include "swift/SIL/SILBuilder.h"
1718
#include "swift/SIL/SILFunction.h"
@@ -295,6 +296,10 @@ static bool canHoistArrayArgument(ApplyInst *SemanticsCall, SILValue Arr,
295296
if (DT->dominates(SelfBB, InsertBefore->getParent()))
296297
return true;
297298

299+
if (auto *Copy = dyn_cast<CopyValueInst>(SelfVal)) {
300+
// look through one level
301+
SelfVal = Copy->getOperand();
302+
}
298303
if (auto LI = dyn_cast<LoadInst>(SelfVal)) {
299304
// Are we loading a value from an address in a struct defined at a point
300305
// dominating the hoist point.
@@ -354,18 +359,28 @@ bool swift::ArraySemanticsCall::canHoist(SILInstruction *InsertBefore,
354359
return false;
355360
}
356361

357-
/// Copy the array load to the insert point.
358-
static SILValue copyArrayLoad(SILValue ArrayStructValue,
359-
SILInstruction *InsertBefore,
360-
DominanceInfo *DT) {
362+
/// Copy the array self value to the insert point.
363+
static SILValue copySelfValue(SILValue ArrayStructValue,
364+
SILInstruction *InsertBefore, DominanceInfo *DT) {
365+
auto *func = InsertBefore->getFunction();
361366
if (DT->dominates(ArrayStructValue->getParentBlock(),
362-
InsertBefore->getParent()))
367+
InsertBefore->getParent())) {
368+
assert(!func->hasOwnership() ||
369+
ArrayStructValue.getOwnershipKind() == OwnershipKind::Owned ||
370+
ArrayStructValue.getOwnershipKind() == OwnershipKind::Guaranteed);
363371
return ArrayStructValue;
372+
}
364373

365-
auto *LI = cast<LoadInst>(ArrayStructValue);
374+
assert(!func->hasOwnership() ||
375+
ArrayStructValue.getOwnershipKind() == OwnershipKind::Owned);
366376

367-
// Recursively move struct_element_addr.
368-
ValueBase *Val = LI->getOperand();
377+
SILValue Val;
378+
if (auto *Load = dyn_cast<LoadInst>(ArrayStructValue)) {
379+
Val = Load->getOperand();
380+
} else {
381+
auto *Copy = cast<CopyValueInst>(ArrayStructValue);
382+
Val = cast<LoadInst>(Copy->getOperand())->getOperand();
383+
}
369384
auto *InsertPt = InsertBefore;
370385
while (!DT->dominates(Val->getParentBlock(), InsertBefore->getParent())) {
371386
auto *Inst = cast<StructElementAddrInst>(Val);
@@ -374,7 +389,16 @@ static SILValue copyArrayLoad(SILValue ArrayStructValue,
374389
InsertPt = Inst;
375390
}
376391

377-
return cast<LoadInst>(LI->clone(InsertBefore));
392+
if (!ArrayStructValue->getFunction()->hasOwnership()) {
393+
return cast<LoadInst>(ArrayStructValue)->clone(InsertBefore);
394+
}
395+
if (auto *Load = dyn_cast<LoadInst>(ArrayStructValue)) {
396+
return Load->clone(InsertBefore);
397+
}
398+
auto *Copy = cast<CopyValueInst>(ArrayStructValue);
399+
auto Addr = cast<LoadInst>(Copy->getOperand())->getOperand();
400+
return SILBuilderWithScope(InsertPt).createLoad(InsertPt->getLoc(), Addr,
401+
LoadOwnershipQualifier::Copy);
378402
}
379403

380404
static ApplyInst *hoistOrCopyCall(ApplyInst *AI, SILInstruction *InsertBefore,
@@ -404,17 +428,16 @@ static SILValue hoistOrCopySelf(ApplyInst *SemanticsCall,
404428

405429
auto Self = SemanticsCall->getSelfArgument();
406430
bool IsOwnedSelf = SelfConvention == ParameterConvention::Direct_Owned;
431+
auto *Func = SemanticsCall->getFunction();
407432

408433
// Emit matching release for owned self if we are moving the original call.
409434
if (!LeaveOriginal && IsOwnedSelf) {
410435
SILBuilderWithScope Builder(SemanticsCall);
411-
Builder.createReleaseValue(SemanticsCall->getLoc(), Self, Builder.getDefaultAtomicity());
436+
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), Self);
412437
}
413-
414-
auto NewArrayStructValue = copyArrayLoad(Self, InsertBefore, DT);
415-
416-
// Retain the array.
417-
if (IsOwnedSelf) {
438+
auto NewArrayStructValue = copySelfValue(Self, InsertBefore, DT);
439+
if (!Func->hasOwnership() && IsOwnedSelf) {
440+
// Retain the array.
418441
SILBuilderWithScope Builder(InsertBefore, SemanticsCall);
419442
Builder.createRetainValue(SemanticsCall->getLoc(), NewArrayStructValue,
420443
Builder.getDefaultAtomicity());
@@ -512,8 +535,7 @@ void swift::ArraySemanticsCall::removeCall() {
512535
if (getSelfParameterConvention(SemanticsCall) ==
513536
ParameterConvention::Direct_Owned) {
514537
SILBuilderWithScope Builder(SemanticsCall);
515-
Builder.createReleaseValue(SemanticsCall->getLoc(), getSelf(),
516-
Builder.getDefaultAtomicity());
538+
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), getSelf());
517539
}
518540

519541
switch (getKind()) {

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ static SILValue getArrayStructPointer(ArrayCallKind K, SILValue Array) {
7878
assert(K != ArrayCallKind::kNone);
7979

8080
if (K < ArrayCallKind::kMakeMutable) {
81-
auto LI = dyn_cast<LoadInst>(Array);
81+
auto LI = dyn_cast<LoadInst>(lookThroughCopyValueInsts(Array));
8282
if (!LI) {
8383
return Array;
8484
}
@@ -107,20 +107,15 @@ mayChangeArraySize(SILInstruction *I, ArrayCallKind &Kind, SILValue &Array,
107107

108108
// TODO: What else.
109109
if (isa<StrongRetainInst>(I) || isa<RetainValueInst>(I) ||
110-
isa<CondFailInst>(I) || isa<DeallocStackInst>(I) ||
111-
isa<AllocationInst>(I))
110+
isa<CopyValueInst>(I) || isa<CondFailInst>(I) ||
111+
isa<DeallocStackInst>(I) || isa<AllocationInst>(I))
112112
return ArrayBoundsEffect::kNone;
113113

114-
// A retain on an arbitrary class can have sideeffects because of the deinit
114+
// A release on an arbitrary class can have sideeffects because of the deinit
115115
// function.
116-
if (auto SR = dyn_cast<StrongReleaseInst>(I))
117-
return isReleaseSafeArrayReference(SR->getOperand(),
118-
ReleaseSafeArrayReferences, RCIA)
119-
? ArrayBoundsEffect::kNone
120-
: ArrayBoundsEffect::kMayChangeAny;
121-
122-
if (auto RV = dyn_cast<ReleaseValueInst>(I))
123-
return isReleaseSafeArrayReference(RV->getOperand(),
116+
if (isa<StrongReleaseInst>(I) || isa<ReleaseValueInst>(I) ||
117+
isa<DestroyValueInst>(I))
118+
return isReleaseSafeArrayReference(I->getOperand(0),
124119
ReleaseSafeArrayReferences, RCIA)
125120
? ArrayBoundsEffect::kNone
126121
: ArrayBoundsEffect::kMayChangeAny;
@@ -148,12 +143,22 @@ mayChangeArraySize(SILInstruction *I, ArrayCallKind &Kind, SILValue &Array,
148143
// A store to an alloc_stack can't possibly store to the array size which is
149144
// stored in a runtime allocated object sub field of an alloca.
150145
if (auto *SI = dyn_cast<StoreInst>(I)) {
146+
if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) {
147+
// store [assign] can call a destructor with unintended effects
148+
return ArrayBoundsEffect::kMayChangeAny;
149+
}
151150
auto Ptr = SI->getDest();
152151
return isa<AllocStackInst>(Ptr) || isAddressOfArrayElement(SI->getDest())
153152
? ArrayBoundsEffect::kNone
154153
: ArrayBoundsEffect::kMayChangeAny;
155154
}
156155

156+
if (isa<LoadInst>(I))
157+
return ArrayBoundsEffect::kNone;
158+
159+
if (isa<BeginBorrowInst>(I) || isa<EndBorrowInst>(I))
160+
return ArrayBoundsEffect::kNone;
161+
157162
return ArrayBoundsEffect::kMayChangeAny;
158163
}
159164

@@ -260,7 +265,7 @@ class ABCAnalysis {
260265
mayChangeArraySize(Inst, K, Array, ReleaseSafeArrayReferences, RCIA);
261266

262267
if (BoundsEffect == ArrayBoundsEffect::kMayChangeAny) {
263-
LLVM_DEBUG(llvm::dbgs() << " no safe because kMayChangeAny " << *Inst);
268+
LLVM_DEBUG(llvm::dbgs() << " not safe because kMayChangeAny " << *Inst);
264269
allArraysInMemoryAreUnsafe = true;
265270
// No need to store specific arrays in this case.
266271
UnsafeArrays.clear();
@@ -420,7 +425,7 @@ static BuiltinValueKind invertCmpID(BuiltinValueKind ID) {
420425
}
421426

422427
/// Checks if Start to End is the range of 0 to the count of an array.
423-
/// Returns the array is this is the case.
428+
/// Returns the array if this is the case.
424429
static SILValue getZeroToCountArray(SILValue Start, SILValue End) {
425430
auto *IL = dyn_cast<IntegerLiteralInst>(Start);
426431
if (!IL || IL->getValue() != 0)
@@ -433,7 +438,6 @@ static SILValue getZeroToCountArray(SILValue Start, SILValue End) {
433438
ArraySemanticsCall SemCall(SEI->getOperand());
434439
if (SemCall.getKind() != ArrayCallKind::kGetCount)
435440
return SILValue();
436-
437441
return SemCall.getSelf();
438442
}
439443

@@ -740,9 +744,9 @@ class AccessFunction {
740744
return nullptr;
741745
}
742746

743-
/// Returns true if the loop iterates from 0 until count of \p Array.
744-
bool isZeroToCount(SILValue Array) {
745-
return getZeroToCountArray(Ind->Start, Ind->End) == Array;
747+
/// Returns true if the loop iterates from 0 until count of \p ArrayVal.
748+
bool isZeroToCount(SILValue ArrayVal) {
749+
return getZeroToCountArray(Ind->Start, Ind->End) == ArrayVal;
746750
}
747751

748752
/// Hoists the necessary check for beginning and end of the induction
@@ -901,11 +905,6 @@ class ABCOpt : public SILFunctionTransform {
901905
return;
902906

903907
SILFunction *F = getFunction();
904-
905-
// FIXME: Update for ownership.
906-
if (F->hasOwnership())
907-
return;
908-
909908
LI = PM->getAnalysis<SILLoopAnalysis>()->get(F);
910909
DT = PM->getAnalysis<DominanceAnalysis>()->get(F);
911910
IVs = PM->getAnalysis<IVAnalysis>()->get(F);
@@ -917,6 +916,8 @@ class ABCOpt : public SILFunctionTransform {
917916
reportBoundsChecks(F);
918917
}
919918
#endif
919+
LLVM_DEBUG(llvm::dbgs()
920+
<< "ArrayBoundsCheckOpts on function: " << F->getName() << "\n");
920921
// Collect all arrays in this function. A release is only 'safe' if we know
921922
// its deinitializer does not have sideeffects that could cause memory
922923
// safety issues. A deinit could deallocate array or put a different array
@@ -931,6 +932,7 @@ class ABCOpt : public SILFunctionTransform {
931932
// that is not calling a deinit function.
932933
if (DestAnalysis->mayStoreToMemoryOnDestruction(rcRoot->getType()))
933934
continue;
935+
LLVM_DEBUG(llvm::dbgs() << "ReleaseSafeArray: " << rcRoot << "\n");
934936
ReleaseSafeArrays.insert(rcRoot);
935937
ReleaseSafeArrays.insert(
936938
getArrayStructPointer(ArrayCallKind::kCheckIndex, rcRoot));

test/SILOptimizer/abcopts.sil

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,13 @@ bb0(%0 : $ArraySlice<Int>):
789789
%t1 = apply %f1(%0) : $@convention(method) (@owned ArraySlice<Int>) -> Int32
790790
%c1 = struct_extract %t1 : $Int32, #Int32._value
791791
%t2 = builtin "cmp_eq_Int32"(%z0 : $Builtin.Int32, %c1 : $Builtin.Int32) : $Builtin.Int1
792-
cond_br %t2, bb2, bb1(%z0 : $Builtin.Int32)
792+
cond_br %t2, bb0a, bb0b
793+
794+
bb0a:
795+
br bb2
796+
797+
bb0b:
798+
br bb1(%z0 : $Builtin.Int32)
793799

794800
bb1(%i0 : $Builtin.Int32):
795801
%f2 = function_ref @checkbounds3 : $@convention(method) (Int32, Bool, @owned ArraySlice<Int>) -> _DependenceToken
@@ -805,7 +811,13 @@ bb1(%i0 : $Builtin.Int32):
805811
%t6 = builtin "sadd_with_overflow_Int32"(%i0 : $Builtin.Int32, %i2 : $Builtin.Int32, %t5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
806812
%t7 = tuple_extract %t6 : $(Builtin.Int32, Builtin.Int1), 0
807813
%8 = builtin "cmp_eq_Int32"(%t7 : $Builtin.Int32, %c1 : $Builtin.Int32) : $Builtin.Int1
808-
cond_br %8, bb2, bb1(%t7 : $Builtin.Int32)
814+
cond_br %8, bb1a, bb1b
815+
816+
bb1a:
817+
br bb2
818+
819+
bb1b:
820+
br bb1(%t7 : $Builtin.Int32)
809821

810822
bb2:
811823
%r1 = tuple ()

0 commit comments

Comments
 (0)