5
5
#include " swift/AST/Type.h"
6
6
#include " swift/SIL/BasicBlockData.h"
7
7
#include " swift/SIL/BasicBlockDatastructures.h"
8
+ #include " swift/SIL/MemAccessUtils.h"
8
9
#include " swift/SIL/SILBasicBlock.h"
9
10
#include " swift/SIL/SILFunction.h"
10
11
#include " swift/SIL/SILInstruction.h"
@@ -61,18 +62,46 @@ class PartitionOpTranslator {
61
62
llvm::DenseMap<const SILNode *, unsigned > nodeIDMap;
62
63
unsigned nextNodeID = 0 ;
63
64
64
- // projectionMap captures "projections" of addresses like begin_borrow
65
- // and begin_access
66
- llvm::DenseMap<SILValue, SILValue> projectionMap;
65
+ AccessStorage getAccessStorageFromAddr (SILValue val) {
66
+ assert (val->getType ().isAddress ());
67
+ auto accessStorage = AccessStorage::compute (val);
68
+ if (accessStorage) {
69
+ if (auto initExistential = dyn_cast_or_null<InitExistentialAddrInst>(
70
+ accessStorage.getRoot ().getDefiningInstruction ()))
71
+ // look through these because AccessStorage does not
72
+ return getAccessStorageFromAddr (initExistential->getOperand ());
73
+ }
74
+ return accessStorage;
75
+ }
76
+
77
+ bool isUniquelyIdentified (SILValue val) {
78
+ if (!val->getType ().isAddress ())
79
+ return false ;
80
+ if (auto accessStorage = getAccessStorageFromAddr (val)) {
81
+ return accessStorage.isUniquelyIdentified ();
82
+ }
83
+ return false ;
84
+ }
85
+
86
+ SILValue simplifyVal (SILValue val) {
87
+ if (!val->getType ().isAddress ())
88
+ return getUnderlyingObject (val);
89
+ if (auto accessStorage = getAccessStorageFromAddr (val)) {
90
+ return accessStorage.getRoot ();
91
+ }
92
+ return val;
93
+ }
67
94
68
95
bool nodeHasID (SILValue value) {
96
+ value = simplifyVal (value);
69
97
assert (isNonSendable (value) &&
70
98
" only non-Sendable values should be entered in the map" );
71
99
return nodeIDMap.count (value);
72
100
}
73
101
74
102
// lookup the internally assigned unique ID of a SILValue, or create one
75
103
unsigned lookupNodeID (SILValue value) {
104
+ value = simplifyVal (value);
76
105
assert (isNonSendable (value) &&
77
106
" only non-Sendable values should be entered in the map" );
78
107
if (nodeIDMap.count (value)) {
@@ -82,22 +111,6 @@ class PartitionOpTranslator {
82
111
return nextNodeID++;
83
112
}
84
113
85
- // note the fact that `tgt` is a projection of `src`, arising from SIL
86
- // instructions such as `tgt = begin_borrow src`. This yields "write-through"
87
- // semantics: `store x to tgt` will then have the same effects as
88
- // `store x to src`.
89
- void addProjection (SILValue tgt, SILValue src) {
90
- projectionMap[tgt] = src;
91
- }
92
-
93
- // lookup `val` to see if it is the target of a projection
94
- llvm::Optional<SILValue> getProjection (SILValue val) {
95
- if (projectionMap.count (val)) {
96
- return projectionMap[val];
97
- }
98
- return {};
99
- }
100
-
101
114
// check the passed type for sendability, special casing the type used for
102
115
// raw pointers to ensure it is treated as non-Sendable and strict checking
103
116
// is applied to it
@@ -116,6 +129,7 @@ class PartitionOpTranslator {
116
129
// to be functions or class methods because these can safely be treated as
117
130
// Sendable despite not having true Sendable type
118
131
bool isNonSendable (SILValue value) {
132
+ value = simplifyVal (value);
119
133
SILInstruction *defInst = value.getDefiningInstruction ();
120
134
if (defInst && isa<ClassMethodInst, FunctionRefInst>(defInst)) {
121
135
// though these values are technically non-Sendable, we can safely
@@ -135,37 +149,46 @@ class PartitionOpTranslator {
135
149
// The following section of functions create fresh PartitionOps referencing
136
150
// the current value of currentInstruction for ease of programming.
137
151
138
- PartitionOp AssignFresh (SILValue value) {
139
- return PartitionOp::AssignFresh (lookupNodeID (value),
140
- currentInstruction);
152
+ std::vector< PartitionOp> AssignFresh (SILValue value) {
153
+ return { PartitionOp::AssignFresh (lookupNodeID (value),
154
+ currentInstruction)} ;
141
155
}
142
156
143
- PartitionOp Assign (SILValue tgt, SILValue src) {
157
+ std::vector< PartitionOp> Assign (SILValue tgt, SILValue src) {
144
158
assert (nodeHasID (src) &&
145
159
" source value of assignment should already have been encountered" );
146
- return PartitionOp::Assign (lookupNodeID (tgt), lookupNodeID (src),
147
- currentInstruction);
160
+
161
+ if (lookupNodeID (tgt) == lookupNodeID (src))
162
+ return {}; // noop
163
+
164
+ return {PartitionOp::Assign (lookupNodeID (tgt), lookupNodeID (src),
165
+ currentInstruction)};
148
166
}
149
167
150
- PartitionOp Consume (SILValue value) {
168
+ std::vector< PartitionOp> Consume (SILValue value) {
151
169
assert (nodeHasID (value) &&
152
170
" consumed value should already have been encountered" );
153
- return PartitionOp::Consume (lookupNodeID (value),
154
- currentInstruction);
171
+
172
+ return {PartitionOp::Consume (lookupNodeID (value),
173
+ currentInstruction)};
155
174
}
156
175
157
- PartitionOp Merge (SILValue fst, SILValue snd) {
176
+ std::vector< PartitionOp> Merge (SILValue fst, SILValue snd) {
158
177
assert (nodeHasID (fst) && nodeHasID (snd) &&
159
178
" merged values should already have been encountered" );
160
- return PartitionOp::Merge (lookupNodeID (fst), lookupNodeID (snd),
161
- currentInstruction);
179
+
180
+ if (lookupNodeID (fst) == lookupNodeID (snd))
181
+ return {}; // noop
182
+
183
+ return {PartitionOp::Merge (lookupNodeID (fst), lookupNodeID (snd),
184
+ currentInstruction)};
162
185
}
163
186
164
- PartitionOp Require (SILValue value) {
187
+ std::vector< PartitionOp> Require (SILValue value) {
165
188
assert (nodeHasID (value) &&
166
189
" required value should already have been encountered" );
167
- return PartitionOp::Require (lookupNodeID (value),
168
- currentInstruction);
190
+ return { PartitionOp::Require (lookupNodeID (value),
191
+ currentInstruction)} ;
169
192
}
170
193
// ===========================================================================
171
194
@@ -233,17 +256,20 @@ class PartitionOpTranslator {
233
256
bool nonSendableResult = isNonSendable (applyInst->getResult (0 ));
234
257
235
258
std::vector<PartitionOp> translated;
259
+ auto add_to_translation = [&](std::vector<PartitionOp> ops) {
260
+ for (auto op : ops) translated.push_back (op);
261
+ };
236
262
237
263
if (SILApplyCrossesIsolation (applyInst)) {
238
264
// for calls that cross isolation domains, consume all operands
239
265
for (SILValue operand : nonSendableOperands)
240
- translated. push_back (Consume (operand));
266
+ add_to_translation (Consume (operand));
241
267
242
268
if (nonSendableResult) {
243
269
// returning non-Sendable values from a cross isolation call will always
244
270
// be an error, but doesn't need to be diagnosed here, so let's pretend
245
271
// it gets a fresh region
246
- translated. push_back (AssignFresh (applyInst->getResult (0 )));
272
+ add_to_translation (AssignFresh (applyInst->getResult (0 )));
247
273
}
248
274
return translated;
249
275
}
@@ -254,83 +280,73 @@ class PartitionOpTranslator {
254
280
if (nonSendableOperands.empty ()) {
255
281
// if no operands, a non-Sendable result gets a fresh region
256
282
if (nonSendableResult) {
257
- translated. push_back (AssignFresh (applyInst->getResult (0 )));
283
+ add_to_translation (AssignFresh (applyInst->getResult (0 )));
258
284
}
259
285
return translated;
260
286
}
261
287
262
288
if (nonSendableOperands.size () == 1 ) {
263
289
// only one operand, so no merges required; just a `Require`
264
- translated. push_back (Require (nonSendableOperands.front ()));
290
+ add_to_translation (Require (nonSendableOperands.front ()));
265
291
} else {
266
292
// merge all operands
267
293
for (unsigned i = 1 ; i < nonSendableOperands.size (); i++) {
268
- translated. push_back (Merge (nonSendableOperands.at (i-1 ),
294
+ add_to_translation (Merge (nonSendableOperands.at (i-1 ),
269
295
nonSendableOperands.at (i)));
270
296
}
271
297
}
272
298
273
299
// if the result is non-Sendable, assign it to the region of the operands
274
300
if (nonSendableResult) {
275
- translated. push_back (
301
+ add_to_translation (
276
302
Assign (applyInst->getResult (0 ), nonSendableOperands.front ()));
277
303
}
278
304
279
305
return translated;
280
306
}
281
307
282
- std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src,
283
- bool projecting = false ) {
308
+ std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src) {
284
309
// no work to be done if assignment is to a Sendable target
285
310
if (!isNonSendable (tgt))
286
311
return {};
287
312
288
313
if (isNonSendable (src)) {
289
- // non-Sendable source and target of assignment
290
-
291
- std::vector<PartitionOp> ops = {};
292
- if (auto projected = getProjection (tgt))
293
- ops = translateSILAssign (projected.value (), src);
294
-
295
- // perform the assignment itself
296
- ops.push_back (Assign (tgt, src));
297
-
298
- // transfer any projections of the source to the target;
299
- if (auto src_projected = getProjection (src))
300
- addProjection (tgt, src_projected.value ());
301
-
302
- // add a projection if requested by the call
303
- if (projecting)
304
- addProjection (tgt, src);
305
-
306
- return ops;
314
+ // non-Sendable source and target of assignment, so just perform the assign
315
+ return Assign (tgt, src);
307
316
}
308
317
309
318
// a non-Sendable value is extracted from a Sendable value,
310
319
// seems to only occur when performing unchecked casts like
311
320
// `unchecked_ref_cast`
312
- return { AssignFresh (tgt)} ;
321
+ return AssignFresh (tgt);
313
322
}
314
323
315
324
// If the passed SILValue is NonSendable, then create a fresh region for it,
316
325
// otherwise do nothing.
317
326
std::vector<PartitionOp> translateSILAssignFresh (SILValue fresh) {
318
327
if (isNonSendable (fresh)) {
319
- return { AssignFresh (fresh)} ;
328
+ return AssignFresh (fresh);
320
329
}
321
330
return {};
322
331
}
323
332
324
333
std::vector<PartitionOp> translateSILMerge (SILValue fst, SILValue snd) {
325
334
if (isNonSendable (fst) && isNonSendable (snd)) {
326
- return { Merge (fst, snd)} ;
335
+ return Merge (fst, snd);
327
336
}
328
337
return {};
329
338
}
330
339
340
+ std::vector<PartitionOp> translateSILStore (SILValue tgt, SILValue src) {
341
+ if (isUniquelyIdentified (tgt)) {
342
+ return translateSILAssign (tgt, src);
343
+ }
344
+ return translateSILMerge (tgt, src);
345
+ }
346
+
331
347
std::vector<PartitionOp> translateSILRequire (SILValue val) {
332
348
if (isNonSendable (val)) {
333
- return { Require (val)} ;
349
+ return Require (val);
334
350
}
335
351
return {};
336
352
}
@@ -359,10 +375,13 @@ class PartitionOpTranslator {
359
375
// (e.g. CopyValueInst, LoadInst, IndexAddrInst) or because the operand
360
376
// is unusable once the result is defined (e.g. the unchecked casts)
361
377
if (isa<AddressToPointerInst,
378
+ BeginAccessInst,
379
+ BeginBorrowInst,
362
380
CopyValueInst,
363
381
ConvertEscapeToNoEscapeInst,
364
382
ConvertFunctionInst,
365
383
IndexAddrInst,
384
+ InitExistentialAddrInst,
366
385
LoadInst,
367
386
LoadBorrowInst,
368
387
LoadWeakInst,
@@ -378,28 +397,14 @@ class PartitionOpTranslator {
378
397
instruction->getOperand (0 ));
379
398
}
380
399
381
- // The following instructions are treated as projecting assignments -
382
- // this means that stores and other writes to their result need to be
383
- // written through to their operand. So far, all instances in which this is
384
- // necessary are shallow and temporary - meaning the projection eventually
385
- // goes out of scope, and the projection can't itself be projected.
386
- if (isa<BeginAccessInst,
387
- BeginBorrowInst,
388
- InitExistentialAddrInst>(instruction)) {
389
- return translateSILAssign (
390
- instruction->getResult (0 ),
391
- instruction->getOperand (0 ),
392
- /* projecting=*/ true );
393
- }
394
-
395
400
// The following instructions are treated as non-projecting assignments,
396
401
// but between their two operands instead of their operand and result.
397
402
if (isa<CopyAddrInst,
398
- ExplicitCopyAddrInst,
403
+ ExplicitCopyAddrInst,
399
404
StoreInst,
400
405
StoreBorrowInst,
401
406
StoreWeakInst>(instruction)) {
402
- return translateSILAssign (
407
+ return translateSILStore (
403
408
instruction->getOperand (1 ),
404
409
instruction->getOperand (0 ));
405
410
}
0 commit comments