Skip to content

Commit 126850e

Browse files
authored
Merge pull request #36540 from eeckstein/fix-temp-rvalue-opt
TempRValueOpt: extend access scopes
2 parents ef75cf7 + 7c454e3 commit 126850e

File tree

3 files changed

+130
-45
lines changed

3 files changed

+130
-45
lines changed

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class TempRValueOptPass : public SILFunctionTransform {
8989
bool
9090
checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst);
9191

92+
bool extendAccessScopes(CopyAddrInst *copyInst, SILInstruction *lastUseInst);
93+
9294
bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst);
9395
std::pair<SILBasicBlock::iterator, bool>
9496
tryOptimizeStoreIntoTemp(StoreInst *si);
@@ -167,12 +169,10 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy,
167169
// %addr = begin_access [read]
168170
// ... // there can be no writes to %addr here
169171
// end_acess %addr // <- This is where the use actually ends.
170-
for (Operand *accessUse : beginAccess->getUses()) {
171-
if (auto *endAccess = dyn_cast<EndAccessInst>(accessUse->getUser())) {
172-
if (endAccess->getParent() != block)
173-
return false;
174-
loadInsts.insert(endAccess);
175-
}
172+
for (EndAccessInst *endAccess : beginAccess->getEndAccesses()) {
173+
if (endAccess->getParent() != block)
174+
return false;
175+
loadInsts.insert(endAccess);
176176
}
177177
return true;
178178
}
@@ -347,6 +347,49 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified(
347347
return nullptr;
348348
}
349349

350+
/// Tries to move an end_access down to extend the access scope over all uses
351+
/// of the temporary. For example:
352+
///
353+
/// %a = begin_access %src
354+
/// copy_addr %a to [initialization] %temp : $*T
355+
/// end_access %a
356+
/// use %temp
357+
///
358+
/// We must not replace %temp with %a after the end_access. Instead we try to
359+
/// move the end_access after "use %temp".
360+
bool TempRValueOptPass::extendAccessScopes(
361+
CopyAddrInst *copyInst, SILInstruction *lastUseInst) {
362+
363+
SILValue copySrc = copyInst->getSrc();
364+
EndAccessInst *endAccessToMove = nullptr;
365+
auto begin = std::next(copyInst->getIterator());
366+
auto end = std::next(lastUseInst->getIterator());
367+
368+
for (SILInstruction &inst : make_range(begin, end)) {
369+
if (auto *endAccess = dyn_cast<EndAccessInst>(&inst)) {
370+
// Is this the end of an access scope of the copy-source?
371+
if (!aa->isNoAlias(copySrc, endAccess->getSource())) {
372+
// To keep things simple, we can just move a single end_access.
373+
if (endAccessToMove)
374+
return false;
375+
endAccessToMove = endAccess;
376+
}
377+
} else if (endAccessToMove) {
378+
// We cannot move an end_access over a begin_access. This would destroy
379+
// the proper nesting of accesses.
380+
if (isa<BeginAccessInst>(&inst))
381+
return false;
382+
// Don't extend a read-access scope over a (potential) write.
383+
if (aa->mayWriteToMemory(&inst, endAccessToMove->getSource()))
384+
return false;
385+
}
386+
}
387+
if (endAccessToMove)
388+
endAccessToMove->moveAfter(lastUseInst);
389+
390+
return true;
391+
}
392+
350393
/// Return true if the \p tempObj, which is initialized by \p copyInst, is
351394
/// destroyed in an orthodox way.
352395
///
@@ -419,13 +462,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
419462

420463
bool isOSSA = copyInst->getFunction()->hasOwnership();
421464

422-
// The copy's source address must not be a scoped instruction, like
423-
// begin_borrow. When the temporary object is eliminated, it's uses are
424-
// replaced with the copy's source. Therefore, the source address must be
425-
// valid at least until the next instruction that may write to or destroy the
426-
// source. End-of-scope markers, such as end_borrow, do not write to or
427-
// destroy memory, so scoped addresses are not valid replacements.
428-
SILValue copySrc = stripAccessMarkers(copyInst->getSrc());
465+
SILValue copySrc = copyInst->getSrc();
429466
assert(tempObj != copySrc && "can't initialize temporary with itself");
430467

431468
// If the source of the copyInst is taken, we must insert a compensating
@@ -487,6 +524,9 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
487524
if (!isOSSA && !checkTempObjectDestroy(tempObj, copyInst))
488525
return false;
489526

527+
if (!extendAccessScopes(copyInst, lastLoadInst))
528+
return false;
529+
490530
LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj);
491531

492532
if (needToInsertDestroy) {
@@ -529,7 +569,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
529569
auto *li = cast<LoadInst>(user);
530570
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take)
531571
li->setOwnershipQualifier(LoadOwnershipQualifier::Copy);
532-
use->set(copyInst->getSrc());
572+
use->set(copySrc);
533573
break;
534574
}
535575

@@ -702,7 +742,7 @@ void TempRValueOptPass::run() {
702742
bool changed = false;
703743

704744
// Find all copy_addr instructions.
705-
llvm::SmallVector<CopyAddrInst *, 8> deadCopies;
745+
llvm::SmallSetVector<CopyAddrInst *, 8> deadCopies;
706746
for (auto &block : *getFunction()) {
707747
// Increment the instruction iterator only after calling
708748
// tryOptimizeCopyIntoTemp because the instruction after CopyInst might be
@@ -716,9 +756,9 @@ void TempRValueOptPass::run() {
716756
// calling tryOptimizeCopyIntoTemp or was created by an earlier
717757
// iteration, where another copy_addr copied the temporary back to the
718758
// source location.
719-
if (stripAccessMarkers(copyInst->getSrc()) == copyInst->getDest()) {
759+
if (copyInst->getSrc() == copyInst->getDest()) {
720760
changed = true;
721-
deadCopies.push_back(copyInst);
761+
deadCopies.insert(copyInst);
722762
}
723763
++ii;
724764
continue;
@@ -735,21 +775,14 @@ void TempRValueOptPass::run() {
735775
}
736776
}
737777

738-
// Delete the copies and any unused address operands.
739-
// The same copy may have been added multiple times.
740-
sortUnique(deadCopies);
741-
InstModCallbacks callbacks{
742-
#ifndef NDEBUG
743-
// With asserts, we include this assert. Otherwise, we use the default
744-
// impl for perf.
745-
[](SILInstruction *instToKill) {
746-
// SimplifyInstruction is not in the business of removing
747-
// copy_addr. If it were, then we would need to update deadCopies.
748-
assert(!isa<CopyAddrInst>(instToKill));
749-
instToKill->eraseFromParent();
750-
}
751-
#endif
752-
};
778+
InstModCallbacks callbacks(
779+
[](SILInstruction *instToKill) {
780+
// SimplifyInstruction is not in the business of removing
781+
// copy_addr. If it were, then we would need to update deadCopies.
782+
assert(!isa<CopyAddrInst>(instToKill));
783+
instToKill->eraseFromParent();
784+
}
785+
);
753786

754787
DeadEndBlocks deBlocks(getFunction());
755788
for (auto *deadCopy : deadCopies) {

test/SILOptimizer/sil_combine_protocol_conf.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,15 @@ public class OtherClass {
238238
// CHECK: bb0([[ARG:%.*]] :
239239
// CHECK: debug_value
240240
// CHECK: [[R1:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg1
241-
// CHECK: [[O1:%.*]] = open_existential_addr immutable_access [[R1]] : $*PropProtocol to $*@opened("{{.*}}") PropProtocol
241+
// CHECK: [[ACC1:%.*]] = begin_access [read] [static] [no_nested_conflict] [[R1]]
242+
// CHECK: [[O1:%.*]] = open_existential_addr immutable_access [[ACC1]] : $*PropProtocol to $*@opened("{{.*}}") PropProtocol
242243
// CHECK: [[U1:%.*]] = unchecked_addr_cast [[O1]] : $*@opened("{{.*}}") PropProtocol to $*PropClass
243244
// CHECK: [[S1:%.*]] = struct_element_addr [[U1]] : $*PropClass, #PropClass.val
244245
// CHECK: [[S11:%.*]] = struct_element_addr [[S1]] : $*Int, #Int._value
245246
// CHECK: load [[S11]]
246247
// CHECK: [[R2:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg2
247-
// CHECK: [[O2:%.*]] = open_existential_addr immutable_access [[R2]] : $*GenericPropProtocol to $*@opened("{{.*}}") GenericPropProtocol
248+
// CHECK: [[ACC2:%.*]] = begin_access [read] [static] [no_nested_conflict] [[R2]]
249+
// CHECK: [[O2:%.*]] = open_existential_addr immutable_access [[ACC2]] : $*GenericPropProtocol to $*@opened("{{.*}}") GenericPropProtocol
248250
// CHECK: [[T1:%.*]] = alloc_stack $@opened
249251
// CHECK: copy_addr [[O2]] to [initialization] [[T1]]
250252
// CHECK: [[W2:%.*]] = witness_method $@opened("{{.*}}") GenericPropProtocol, #GenericPropProtocol.val!getter : <Self where Self : GenericPropProtocol> (Self) -> () -> Int, [[O2]] : $*@opened("{{.*}}") GenericPropProtocol : $@convention(witness_method: GenericPropProtocol) <τ_0_0 where τ_0_0 : GenericPropProtocol> (@in_guaranteed τ_0_0) -> Int
@@ -256,7 +258,8 @@ public class OtherClass {
256258
// CHECK: tuple_extract
257259
// CHECK: cond_fail
258260
// CHECK: [[R4:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg3
259-
// CHECK: [[O4:%.*]] = open_existential_addr immutable_access [[R4]] : $*NestedPropProtocol to $*@opened("{{.*}}") NestedPropProtocol
261+
// CHECK: [[ACC4:%.*]] = begin_access [read] [static] [no_nested_conflict] [[R4]]
262+
// CHECK: [[O4:%.*]] = open_existential_addr immutable_access [[ACC4]] : $*NestedPropProtocol to $*@opened("{{.*}}") NestedPropProtocol
260263
// CHECK: [[U4:%.*]] = unchecked_addr_cast [[O4]] : $*@opened("{{.*}}") NestedPropProtocol to $*Outer.Inner
261264
// CHECK: [[S4:%.*]] = struct_element_addr [[U4]] : $*Outer.Inner, #Outer.Inner.val
262265
// CHECK: [[S41:%.*]] = struct_element_addr [[S4]] : $*Int, #Int._value
@@ -266,7 +269,8 @@ public class OtherClass {
266269
// CHECK: tuple_extract
267270
// CHECK: cond_fail
268271
// CHECK: [[R5:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg4
269-
// CHECK: [[O5:%.*]] = open_existential_addr immutable_access [[R5]] : $*GenericNestedPropProtocol to $*@opened("{{.*}}") GenericNestedPropProtocol
272+
// CHECK: [[ACC5:%.*]] = begin_access [read] [static] [no_nested_conflict] [[R5]]
273+
// CHECK: [[O5:%.*]] = open_existential_addr immutable_access [[ACC5]] : $*GenericNestedPropProtocol to $*@opened("{{.*}}") GenericNestedPropProtocol
270274
// CHECK: [[T2:%.*]] = alloc_stack $@opened
271275
// CHECK: copy_addr [[O5]] to [initialization] [[T2]]
272276
// CHECK: [[W5:%.*]] = witness_method $@opened("{{.*}}") GenericNestedPropProtocol, #GenericNestedPropProtocol.val!getter : <Self where Self : GenericNestedPropProtocol> (Self) -> () -> Int, [[O5:%.*]] : $*@opened("{{.*}}") GenericNestedPropProtocol : $@convention(witness_method: GenericNestedPropProtocol) <τ_0_0 where τ_0_0 : GenericNestedPropProtocol> (@in_guaranteed τ_0_0) -> Int
@@ -336,7 +340,8 @@ public class OtherKlass {
336340
// CHECK: debug_value
337341
// CHECK: integer_literal
338342
// CHECK: [[R1:%.*]] = ref_element_addr [[ARG]] : $OtherKlass, #OtherKlass.arg2
339-
// CHECK: [[O1:%.*]] = open_existential_addr immutable_access [[R1]] : $*AGenericProtocol to $*@opened("{{.*}}") AGenericProtocol
343+
// CHECK: [[ACC1:%.*]] = begin_access [read] [static] [no_nested_conflict] [[R1]]
344+
// CHECK: [[O1:%.*]] = open_existential_addr immutable_access [[ACC1]] : $*AGenericProtocol to $*@opened("{{.*}}") AGenericProtocol
340345
// CHECK: [[T1:%.*]] = alloc_stack $@opened
341346
// CHECK: copy_addr [[O1]] to [initialization] [[T1]]
342347
// CHECK: [[W1:%.*]] = witness_method $@opened("{{.*}}") AGenericProtocol, #AGenericProtocol.val!getter : <Self where Self : AGenericProtocol> (Self) -> () -> Int, [[O1]] : $*@opened("{{.*}}") AGenericProtocol : $@convention(witness_method: AGenericProtocol) <τ_0_0 where τ_0_0 : AGenericProtocol> (@in_guaranteed τ_0_0) -> Int

test/SILOptimizer/temp_rvalue_opt_ossa.sil

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ bb0(%0 : $*Klass, %1 : $*Klass):
4242
sil @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error)
4343
sil @use_gsbase_builtinnativeobject : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
4444

45+
sil_global @globalString : $String
46+
4547
///////////
4648
// Tests //
4749
///////////
@@ -830,16 +832,61 @@ bb3(%obj3 : @owned $GS<Builtin.NativeObject>):
830832
return %obj3 : $GS<Builtin.NativeObject>
831833
}
832834

833-
// Test copy elimination with access markers on both the source and dest.
834-
//
835-
// CHECK-LABEL: sil [ossa] @takeWithAccess : $@convention(thin) (@in Klass) -> () {
835+
// CHECK-LABEL: sil [ossa] @extendAccessScopeOverLoad
836+
// CHECK: [[GLOBAL:%.*]] = global_addr @globalString
837+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL]]
838+
// CHECK: [[LOAD:%.*]] = load [copy] [[ACCESS]]
839+
// CHECK: end_access [[ACCESS]]
840+
// CHECK: return [[LOAD]]
841+
// CHECK-LABEL: } // end sil function 'extendAccessScopeOverLoad'
842+
sil [ossa] @extendAccessScopeOverLoad : $@convention(thin) () -> @owned String {
843+
bb0:
844+
%1 = global_addr @globalString : $*String
845+
%3 = begin_access [read] [dynamic] %1 : $*String
846+
%4 = alloc_stack $String
847+
copy_addr %3 to [initialization] %4 : $*String
848+
end_access %3 : $*String
849+
%6 = load [copy] %4 : $*String
850+
destroy_addr %4 : $*String
851+
dealloc_stack %4 : $*String
852+
return %6 : $String
853+
}
854+
855+
sil [ossa] @loadString : $@convention(thin) (@in_guaranteed String) -> @owned String {
856+
bb0(%0 : $*String):
857+
%1 = load [copy] %0 : $*String
858+
return %1 : $String
859+
}
860+
861+
// CHECK-LABEL: sil [ossa] @extendAccessScopeOverApply
862+
// CHECK: [[GLOBAL:%.*]] = global_addr @globalString
863+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL]]
864+
// CHECK: [[RESULT:%.*]] = apply {{%[0-9]+}}([[ACCESS]])
865+
// CHECK: end_access [[ACCESS]]
866+
// CHECK: return [[RESULT]]
867+
// CHECK-LABEL: } // end sil function 'extendAccessScopeOverApply'
868+
sil [ossa] @extendAccessScopeOverApply : $@convention(thin) () -> @owned String {
869+
bb0:
870+
%1 = global_addr @globalString : $*String
871+
%3 = begin_access [read] [dynamic] %1 : $*String
872+
%4 = alloc_stack $String
873+
copy_addr %3 to [initialization] %4 : $*String
874+
end_access %3 : $*String
875+
%f = function_ref @loadString : $@convention(thin) (@in_guaranteed String) -> @owned String
876+
%a = apply %f(%4) : $@convention(thin) (@in_guaranteed String) -> @owned String
877+
destroy_addr %4 : $*String
878+
dealloc_stack %4 : $*String
879+
return %a : $String
880+
}
881+
882+
// CHECK-LABEL: sil [ossa] @dontExtendAccessScopeOverBeginAccess : $@convention(thin) (@in Klass) -> () {
836883
// CHECK: bb0(%0 : $*Klass):
837-
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0 : $*Klass
884+
// CHECK: [[STACK:%.*]] = alloc_stack $Klass
885+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] [[STACK]] : $*Klass
838886
// CHECK: apply %{{.*}}([[ACCESS]]) : $@convention(thin) (@in_guaranteed Klass) -> ()
839-
// CHECK: end_access [[ACCESS]] : $*Klass
840-
// CHECK: destroy_addr %0 : $*Klass
841-
// CHECK-LABEL: } // end sil function 'takeWithAccess'
842-
sil [ossa] @takeWithAccess : $@convention(thin) (@in Klass) -> () {
887+
// CHECK: destroy_addr [[STACK]] : $*Klass
888+
// CHECK-LABEL: } // end sil function 'dontExtendAccessScopeOverBeginAccess'
889+
sil [ossa] @dontExtendAccessScopeOverBeginAccess : $@convention(thin) (@in Klass) -> () {
843890
bb0(%0 : $*Klass):
844891
%stack = alloc_stack $Klass
845892
%access = begin_access [read] [static] %0 : $*Klass

0 commit comments

Comments
 (0)