Skip to content

Commit f22864e

Browse files
authored
Merge pull request #19181 from eeckstein/array-opts
SILOptimizer: update bounds check and uniqueness check hoisting optimizations for using _modify in Array subscript.
2 parents d6133f4 + 584ed97 commit f22864e

File tree

6 files changed

+110
-54
lines changed

6 files changed

+110
-54
lines changed

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index);
123123
/// 'Self' to a generic argument of the callee.
124124
bool mayBindDynamicSelf(SILFunction *F);
125125

126+
/// Check whether the \p addr is an address of a tail-allocated array element.
127+
bool isAddressOfArrayElement(SILValue addr);
128+
126129
/// \brief Move an ApplyInst's FuncRef so that it dominates the call site.
127130
void placeFuncRef(ApplyInst *AI, DominanceInfo *DT);
128131

lib/SILOptimizer/IPO/UsePrespecialized.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
148148
} else if (auto oldPApply = dyn_cast<PartialApplyInst>(AI)) {
149149
oldPApply->replaceAllUsesWith(cast<PartialApplyInst>(NewAI));
150150
} else {
151-
assert(isa<TryApplyInst>(NewAI));
151+
assert(isa<TryApplyInst>(NewAI) || isa<BeginApplyInst>(NewAI));
152152
}
153153
recursivelyDeleteTriviallyDeadInstructions(AI.getInstruction(), true);
154154
Changed = true;

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,42 +87,6 @@ static SILValue getArrayStructPointer(ArrayCallKind K, SILValue Array) {
8787
return Array;
8888
}
8989

90-
/// Check whether the store is to the address obtained from a getElementAddress
91-
/// semantic call.
92-
/// %40 = function_ref @getElementAddress
93-
/// %42 = apply %40(%28, %37)
94-
/// %43 = struct_extract %42
95-
/// %44 = pointer_to_address strict %43
96-
/// store %1 to %44 : $*Int
97-
static bool isArrayEltStore(StoreInst *SI) {
98-
// Strip the MarkDependenceInst (new array implementation) where the above
99-
// pattern looks like the following.
100-
// %40 = function_ref @getElementAddress
101-
// %41 = apply %40(%21, %35)
102-
// %42 = struct_element_addr %0 : $*Array<Int>, #Array._buffer
103-
// %43 = struct_element_addr %42 : $*_ArrayBuffer<Int>, #_ArrayBuffer._storage
104-
// %44 = struct_element_addr %43 : $*_BridgeStorage
105-
// %45 = load %44 : $*Builtin.BridgeObject
106-
// %46 = unchecked_ref_cast %45 : $... to $_ContiguousArrayStorageBase
107-
// %47 = unchecked_ref_cast %46 : $... to $Builtin.NativeObject
108-
// %48 = struct_extract %41 : $..., #UnsafeMutablePointer._rawValue
109-
// %49 = pointer_to_address %48 : $Builtin.RawPointer to strict $*Int
110-
// %50 = mark_dependence %49 : $*Int on %47 : $Builtin.NativeObject
111-
// store %1 to %50 : $*Int
112-
SILValue Dest = SI->getDest();
113-
if (auto *MD = dyn_cast<MarkDependenceInst>(Dest))
114-
Dest = MD->getOperand(0);
115-
116-
if (auto *PtrToAddr =
117-
dyn_cast<PointerToAddressInst>(stripAddressProjections(Dest)))
118-
if (auto *SEI = dyn_cast<StructExtractInst>(PtrToAddr->getOperand())) {
119-
ArraySemanticsCall Call(SEI->getOperand());
120-
if (Call && Call.getKind() == ArrayCallKind::kGetElementAddress)
121-
return true;
122-
}
123-
return false;
124-
}
125-
12690
static bool isReleaseSafeArrayReference(SILValue Ref,
12791
ArraySet &ReleaseSafeArrayReferences,
12892
RCIdentityFunctionInfo *RCIA) {
@@ -185,7 +149,7 @@ mayChangeArraySize(SILInstruction *I, ArrayCallKind &Kind, SILValue &Array,
185149
// stored in a runtime allocated object sub field of an alloca.
186150
if (auto *SI = dyn_cast<StoreInst>(I)) {
187151
auto Ptr = SI->getDest();
188-
return isa<AllocStackInst>(Ptr) || isArrayEltStore(SI)
152+
return isa<AllocStackInst>(Ptr) || isAddressOfArrayElement(SI->getDest())
189153
? ArrayBoundsEffect::kNone
190154
: ArrayBoundsEffect::kMayChangeAny;
191155
}

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -798,20 +798,6 @@ bool COWArrayOpt::checkSafeElementValueUses(UserOperList &ElementValueUsers) {
798798
}
799799
return true;
800800
}
801-
static bool isArrayEltStore(StoreInst *SI) {
802-
SILValue Dest = stripAddressProjections(SI->getDest());
803-
if (auto *MD = dyn_cast<MarkDependenceInst>(Dest))
804-
Dest = MD->getValue();
805-
806-
if (auto *PtrToAddr =
807-
dyn_cast<PointerToAddressInst>(stripAddressProjections(Dest)))
808-
if (auto *SEI = dyn_cast<StructExtractInst>(PtrToAddr->getOperand())) {
809-
ArraySemanticsCall Call(SEI->getOperand());
810-
if (Call && Call.getKind() == ArrayCallKind::kGetElementAddress)
811-
return true;
812-
}
813-
return false;
814-
}
815801

816802
/// Check whether the array semantic operation could change an array value to
817803
/// not be uniquely referenced.
@@ -1278,7 +1264,7 @@ bool COWArrayOpt::hoistInLoopWithOnlyNonArrayValueMutatingOperations() {
12781264
// A store is only safe if it is to an array element and the element type
12791265
// is trivial.
12801266
if (auto *SI = dyn_cast<StoreInst>(Inst)) {
1281-
if (!isArrayEltStore(SI) ||
1267+
if (!isAddressOfArrayElement(SI->getDest()) ||
12821268
!SI->getSrc()->getType().isTrivial(Module)) {
12831269
LLVM_DEBUG(llvm::dbgs()
12841270
<<" (NO) non trivial store could store an array value "
@@ -1420,7 +1406,7 @@ bool COWArrayOpt::hasLoopOnlyDestructorSafeArrayOperations() {
14201406

14211407
// Stores to array elements.
14221408
if (auto *SI = dyn_cast<StoreInst>(Inst)) {
1423-
if (isArrayEltStore(SI))
1409+
if (isAddressOfArrayElement(SI->getDest()))
14241410
continue;
14251411
LLVM_DEBUG(llvm::dbgs() << " (NO) unknown store " << *SI);
14261412
return ReturnWithCleanup(false);

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,43 @@ bool swift::mayBindDynamicSelf(SILFunction *F) {
329329
return false;
330330
}
331331

332+
static SILValue skipAddrProjections(SILValue V) {
333+
for (;;) {
334+
switch (V->getKind()) {
335+
case ValueKind::IndexAddrInst:
336+
case ValueKind::IndexRawPointerInst:
337+
case ValueKind::StructElementAddrInst:
338+
case ValueKind::TupleElementAddrInst:
339+
V = cast<SingleValueInstruction>(V)->getOperand(0);
340+
break;
341+
default:
342+
return V;
343+
}
344+
}
345+
llvm_unreachable("there is no escape from an infinite loop");
346+
}
347+
348+
/// Check whether the \p addr is an address of a tail-allocated array element.
349+
bool swift::isAddressOfArrayElement(SILValue addr) {
350+
addr = stripAddressProjections(addr);
351+
if (auto *MD = dyn_cast<MarkDependenceInst>(addr))
352+
addr = stripAddressProjections(MD->getValue());
353+
354+
// High-level SIL: check for an get_element_address array semantics call.
355+
if (auto *PtrToAddr = dyn_cast<PointerToAddressInst>(addr))
356+
if (auto *SEI = dyn_cast<StructExtractInst>(PtrToAddr->getOperand())) {
357+
ArraySemanticsCall Call(SEI->getOperand());
358+
if (Call && Call.getKind() == ArrayCallKind::kGetElementAddress)
359+
return true;
360+
}
361+
362+
// Check for an tail-address (of an array buffer object).
363+
if (isa<RefTailAddrInst>(skipAddrProjections(addr)))
364+
return true;
365+
366+
return false;
367+
}
368+
332369
/// Find a new position for an ApplyInst's FuncRef so that it dominates its
333370
/// use. Not that FunctionRefInsts may be shared by multiple ApplyInsts.
334371
void swift::placeFuncRef(ApplyInst *AI, DominanceInfo *DT) {

test/SILOptimizer/abcopts.sil

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ struct ArrayIntBuffer {
1212
var storage : Builtin.NativeObject
1313
}
1414

15+
final class StorageBase {
16+
@sil_stored var header: Int64
17+
}
18+
1519
struct ArrayInt{
1620
var buffer : ArrayIntBuffer
1721
}
@@ -665,6 +669,68 @@ bb3:
665669
return %23 : $Int32
666670
}
667671

672+
// HOIST-LABEL: sil @hoist_rangechecked_ref_tail_addr
673+
// HOIST: bb0
674+
// HOIST: cond_br {{.*}}, bb1{{.*}}, bb2
675+
// HOIST: bb1:
676+
// HOIST: br bb6
677+
// HOIST: bb2:
678+
// HOIST: [[CB:%[0-9]+]] = function_ref @checkbounds
679+
// HOIST: apply [[CB]]
680+
// HOIST: br bb3{{.*}}
681+
// HOIST: bb3{{.*}}:
682+
// HOIST-NOT: function_ref @checkbounds
683+
// HOIST-NOT: apply [[CB]]
684+
// HOIST: cond_br {{.*}}, bb5{{.*}}, bb4{{.*}}
685+
// HOIST: bb4
686+
// HOIST: br bb3
687+
// HOIST: bb5
688+
// HOIST: br bb6
689+
// HOIST: bb6{{.*}}:
690+
// HOIST: return
691+
692+
sil @hoist_rangechecked_ref_tail_addr : $@convention(thin) (Int32, @inout ArrayInt) -> Int32 {
693+
bb0(%0 : $Int32, %24 : $*ArrayInt):
694+
%100 = integer_literal $Builtin.Int1, -1
695+
%101 = struct $Bool(%100 : $Builtin.Int1)
696+
%1 = struct_extract %0 : $Int32, #Int32._value
697+
%2 = integer_literal $Builtin.Int32, 0
698+
%3 = integer_literal $Builtin.Int1, -1
699+
%61 = builtin "cmp_sle_Int32"(%2 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
700+
%14 = builtin "xor_Int1"(%61 : $Builtin.Int1, %3 : $Builtin.Int1) : $Builtin.Int1
701+
cond_fail %14 : $Builtin.Int1
702+
br bb1(%2 : $Builtin.Int32)
703+
704+
bb1(%4 : $Builtin.Int32):
705+
%8 = builtin "cmp_eq_Int32"(%4 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
706+
cond_br %8, bb3, bb4
707+
708+
bb4:
709+
%37 = struct $Int32(%4 : $Builtin.Int32)
710+
%52 = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
711+
%53 = load %24 : $*ArrayInt
712+
%54 = struct_extract %53 : $ArrayInt, #ArrayInt.buffer
713+
%55 = struct_extract %54 : $ArrayIntBuffer, #ArrayIntBuffer.storage
714+
retain_value %55 : $Builtin.NativeObject
715+
%58 = apply %52(%37, %101, %53) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
716+
%10 = integer_literal $Builtin.Int32, 1
717+
%19 = integer_literal $Builtin.Int1, -1
718+
%20 = builtin "sadd_with_overflow_Int32"(%4 : $Builtin.Int32, %10 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
719+
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
720+
721+
retain_value %55 : $Builtin.NativeObject
722+
723+
%69 = unchecked_ref_cast %55 : $Builtin.NativeObject to $StorageBase
724+
%70 = ref_tail_addr %69 : $StorageBase, $Int32
725+
%71 = index_addr %70 : $*Int32, %4 : $Builtin.Int32
726+
store %0 to %71 : $*Int32
727+
br bb1(%21 : $Builtin.Int32)
728+
729+
bb3:
730+
%23 = struct $Int32 (%4 : $Builtin.Int32)
731+
return %23 : $Int32
732+
}
733+
668734
// HOIST-LABEL: sil @eliminate_zero_to_count
669735
// HOIST-NOT: function_ref @checkbounds2
670736
// HOIST: return

0 commit comments

Comments
 (0)