2
2
//
3
3
// This source file is part of the Swift.org open source project
4
4
//
5
- // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5
+ // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
44
44
45
45
#define DEBUG_TYPE " sil-capture-promotion"
46
46
47
+ #include " swift/AST/DiagnosticsSIL.h"
47
48
#include " swift/AST/GenericEnvironment.h"
49
+ #include " swift/Basic/FrozenMultiMap.h"
48
50
#include " swift/SIL/SILCloner.h"
49
51
#include " swift/SIL/SILInstruction.h"
50
52
#include " swift/SIL/TypeSubstCloner.h"
56
58
#include " llvm/ADT/SmallSet.h"
57
59
#include " llvm/ADT/Statistic.h"
58
60
#include " llvm/Support/Debug.h"
61
+ #include " llvm/Support/ErrorHandling.h"
59
62
#include < tuple>
60
63
61
64
using namespace swift ;
@@ -765,6 +768,15 @@ struct EscapeMutationScanningState {
765
768
// / found.
766
769
SmallVector<Operand *, 8 > accumulatedEscapes;
767
770
771
+ // / A multimap that maps partial applies to the set of operands in the partial
772
+ // / applies referenced function that the pass has identified as being the use
773
+ // / that caused the partial apply to capture our box.
774
+ // /
775
+ // / We use a frozen multi-map since our algorithm first accumulates this info
776
+ // / and then wants to use it, perfect for the 2-stage frozen multi map.
777
+ SmallFrozenMultiMap<PartialApplyInst *, Operand *, 16 >
778
+ accumulatedCaptureCausingUses;
779
+
768
780
// / A flag that we use to ensure that we only ever see 1 project_box on an
769
781
// / alloc_box.
770
782
bool sawProjectBoxInst;
@@ -797,15 +809,17 @@ static bool isNonMutatingLoad(SILInstruction *inst) {
797
809
// / address of the box's contents), return true if this box has mutating
798
810
// / captures. Return false otherwise. All of the mutating captures that we find
799
811
// / are placed into \p accumulatedMutatingUses.
800
- static bool getPartialApplyArgMutationsAndEscapes (
801
- SILArgument *boxArg, SmallVectorImpl<Operand *> &accumulatedMutatingUses,
802
- SmallVectorImpl<Operand *> &accumulatedEscapes) {
812
+ static bool
813
+ getPartialApplyArgMutationsAndEscapes (PartialApplyInst *pai,
814
+ SILArgument *boxArg,
815
+ EscapeMutationScanningState &state) {
803
816
SmallVector<ProjectBoxInst *, 2 > projectBoxInsts;
804
817
805
818
// Conservatively do not allow any use of the box argument other than a
806
819
// strong_release or projection, since this is the pattern expected from
807
820
// SILGen.
808
821
SmallVector<Operand *, 32 > incrementalEscapes;
822
+ SmallVector<Operand *, 32 > incrementalCaptureCausingUses;
809
823
for (auto *use : boxArg->getUses ()) {
810
824
if (isa<StrongReleaseInst>(use->getUser ()) ||
811
825
isa<DestroyValueInst>(use->getUser ()))
@@ -827,18 +841,25 @@ static bool getPartialApplyArgMutationsAndEscapes(
827
841
// function that mirrors isNonEscapingUse.
828
842
auto checkIfAddrUseMutating = [&](Operand *addrUse) -> bool {
829
843
unsigned initSize = incrementalEscapes.size ();
830
- auto *addrInst = addrUse->getUser ();
831
- if (auto *seai = dyn_cast<StructElementAddrInst>(addrInst )) {
844
+ auto *addrUser = addrUse->getUser ();
845
+ if (auto *seai = dyn_cast<StructElementAddrInst>(addrUser )) {
832
846
for (auto *seaiUse : seai->getUses ()) {
833
- if (!isNonMutatingLoad (seaiUse->getUser ())) {
847
+ if (isNonMutatingLoad (seaiUse->getUser ())) {
848
+ incrementalCaptureCausingUses.push_back (seaiUse);
849
+ } else {
834
850
incrementalEscapes.push_back (seaiUse);
835
851
}
836
852
}
837
853
return incrementalEscapes.size () != initSize;
838
854
}
839
855
840
- if (isNonMutatingLoad (addrInst) || isa<DebugValueAddrInst>(addrInst) ||
841
- isa<MarkFunctionEscapeInst>(addrInst) || isa<EndAccessInst>(addrInst)) {
856
+ if (isNonMutatingLoad (addrUser)) {
857
+ incrementalCaptureCausingUses.push_back (addrUse);
858
+ return false ;
859
+ }
860
+
861
+ if (isa<DebugValueAddrInst>(addrUser) ||
862
+ isa<MarkFunctionEscapeInst>(addrUser) || isa<EndAccessInst>(addrUser)) {
842
863
return false ;
843
864
}
844
865
@@ -859,10 +880,15 @@ static bool getPartialApplyArgMutationsAndEscapes(
859
880
}
860
881
}
861
882
883
+ auto &accCaptureCausingUses = state.accumulatedCaptureCausingUses ;
884
+ while (!incrementalCaptureCausingUses.empty ())
885
+ accCaptureCausingUses.insert (pai,
886
+ incrementalCaptureCausingUses.pop_back_val ());
887
+
862
888
if (incrementalEscapes.empty ())
863
889
return false ;
864
890
while (!incrementalEscapes.empty ())
865
- accumulatedEscapes.push_back (incrementalEscapes.pop_back_val ());
891
+ state. accumulatedEscapes .push_back (incrementalEscapes.pop_back_val ());
866
892
return true ;
867
893
}
868
894
@@ -925,8 +951,7 @@ bool isPartialApplyNonEscapingUser(Operand *currentOp, PartialApplyInst *pai,
925
951
// Verify that this closure is known not to mutate the captured value; if
926
952
// it does, then conservatively refuse to promote any captures of this
927
953
// value.
928
- if (getPartialApplyArgMutationsAndEscapes (boxArg, state.accumulatedMutations ,
929
- state.accumulatedEscapes )) {
954
+ if (getPartialApplyArgMutationsAndEscapes (pai, boxArg, state)) {
930
955
LLVM_DEBUG (llvm::dbgs () << " FAIL: Have a mutation or escape of a "
931
956
" partial apply arg?!\n " );
932
957
return false ;
@@ -1195,6 +1220,57 @@ static bool findEscapeOrMutationUses(Operand *op,
1195
1220
return isNonEscapingUse (op, state);
1196
1221
}
1197
1222
1223
+ // / We found a capture of \p abi in concurrent closure \p pai that we can not
1224
+ // / promote to a by value capture. Emit a nice warning (FIXME: error) to warn
1225
+ // / the user and provide the following information in the compiler feedback:
1226
+ // /
1227
+ // / 1. The source loc where the variable's box is written to.
1228
+ // /
1229
+ // / 2. The source loc of the captured variable's declaration.
1230
+ // /
1231
+ // / 3. The source loc of the start of the concurrent closure that caused the
1232
+ // / variable to be captured.
1233
+ // /
1234
+ // / 4. All places in the concurrent closure that triggered the box's
1235
+ // / capture. NOTE: For objects these are load points. For address only things
1236
+ // / it is still open for debate at this point.
1237
+ static void diagnoseInvalidCaptureByConcurrentClosure (
1238
+ AllocBoxInst *abi, PartialApplyInst *pai,
1239
+ const EscapeMutationScanningState &state, SILInstruction *mutatingUser) {
1240
+ auto captureCausingUses = state.accumulatedCaptureCausingUses .find (pai);
1241
+ if (!captureCausingUses) {
1242
+ llvm::errs () << " Didn't find capture causing use of partial apply: "
1243
+ << *pai;
1244
+ llvm::errs () << " Original Func: " << pai->getFunction ()->getName () << ' \n ' ;
1245
+ llvm::errs () << " Partial Applied Func: "
1246
+ << pai->getReferencedFunctionOrNull ()->getName () << ' \n ' ;
1247
+ llvm::report_fatal_error (" standard compiler error" );
1248
+ }
1249
+
1250
+ auto &astCtx = pai->getFunction ()->getASTContext ();
1251
+ auto &de = astCtx.Diags ;
1252
+ auto varInfo = abi->getVarInfo ();
1253
+ StringRef name = " <unknown>" ;
1254
+ if (varInfo) {
1255
+ name = varInfo->Name ;
1256
+ }
1257
+
1258
+ de.diagnoseWithNotes (
1259
+ de.diagnose (mutatingUser->getLoc ().getSourceLoc (),
1260
+ diag::capturepromotion_concurrentcapture_mutation, name),
1261
+ [&]() {
1262
+ de.diagnose (abi->getLoc ().getSourceLoc (),
1263
+ diag::capturepromotion_variable_defined_here);
1264
+ de.diagnose (pai->getLoc ().getSourceLoc (),
1265
+ diag::capturepromotion_concurrentcapture_closure_here);
1266
+ for (auto *use : *captureCausingUses) {
1267
+ de.diagnose (
1268
+ use->getUser ()->getLoc ().getSourceLoc (),
1269
+ diag::capturepromotion_concurrentcapture_capturinguse_here);
1270
+ }
1271
+ });
1272
+ }
1273
+
1198
1274
// / Examine an alloc_box instruction, returning true if at least one
1199
1275
// / capture of the boxed variable is promotable. If so, then the pair of the
1200
1276
// / partial_apply instruction and the index of the box argument in the closure's
@@ -1203,7 +1279,7 @@ static bool
1203
1279
examineAllocBoxInst (AllocBoxInst *abi, ReachabilityInfo &ri,
1204
1280
llvm::DenseMap<PartialApplyInst *, unsigned > &im) {
1205
1281
LLVM_DEBUG (llvm::dbgs () << " Visiting alloc box: " << *abi);
1206
- EscapeMutationScanningState state{{}, {}, false , im};
1282
+ EscapeMutationScanningState state{{}, {}, {}, false , im};
1207
1283
1208
1284
// Scan the box for escaping or mutating uses.
1209
1285
for (auto *use : abi->getUses ()) {
@@ -1220,6 +1296,7 @@ examineAllocBoxInst(AllocBoxInst *abi, ReachabilityInfo &ri,
1220
1296
return false ;
1221
1297
}
1222
1298
1299
+ state.accumulatedCaptureCausingUses .setFrozen ();
1223
1300
LLVM_DEBUG (llvm::dbgs () << " We can optimize this alloc box!\n " );
1224
1301
1225
1302
// Helper lambda function to determine if instruction b is strictly after
@@ -1249,6 +1326,13 @@ examineAllocBoxInst(AllocBoxInst *abi, ReachabilityInfo &ri,
1249
1326
// block is after the partial_apply.
1250
1327
if (ri.isReachable (pai->getParent (), user->getParent ()) ||
1251
1328
(pai->getParent () == user->getParent () && isAfter (pai, user))) {
1329
+ // If our partial apply is concurrent and we can not promote this, emit
1330
+ // a warning that shows the variable, where the variable is captured,
1331
+ // and the mutation that we found.
1332
+ if (pai->getFunctionType ()->isConcurrent ()) {
1333
+ diagnoseInvalidCaptureByConcurrentClosure (abi, pai, state, user);
1334
+ }
1335
+
1252
1336
LLVM_DEBUG (llvm::dbgs () << " Invalidating: " << *pai);
1253
1337
LLVM_DEBUG (llvm::dbgs () << " Because of user: " << *user);
1254
1338
auto prev = iter++;
@@ -1257,6 +1341,7 @@ examineAllocBoxInst(AllocBoxInst *abi, ReachabilityInfo &ri,
1257
1341
}
1258
1342
++iter;
1259
1343
}
1344
+
1260
1345
// If there are no valid captures left, then stop.
1261
1346
if (im.empty ()) {
1262
1347
LLVM_DEBUG (llvm::dbgs () << " Ran out of valid captures... bailing!\n " );
0 commit comments