55#include " swift/AST/Type.h"
66#include " swift/SIL/BasicBlockData.h"
77#include " swift/SIL/BasicBlockDatastructures.h"
8+ #include " swift/SIL/MemAccessUtils.h"
89#include " swift/SIL/SILBasicBlock.h"
910#include " swift/SIL/SILFunction.h"
1011#include " swift/SIL/SILInstruction.h"
@@ -61,18 +62,46 @@ class PartitionOpTranslator {
6162 llvm::DenseMap<const SILNode *, unsigned > nodeIDMap;
6263 unsigned nextNodeID = 0 ;
6364
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+ }
6794
6895 bool nodeHasID (SILValue value) {
96+ value = simplifyVal (value);
6997 assert (isNonSendable (value) &&
7098 " only non-Sendable values should be entered in the map" );
7199 return nodeIDMap.count (value);
72100 }
73101
74102 // lookup the internally assigned unique ID of a SILValue, or create one
75103 unsigned lookupNodeID (SILValue value) {
104+ value = simplifyVal (value);
76105 assert (isNonSendable (value) &&
77106 " only non-Sendable values should be entered in the map" );
78107 if (nodeIDMap.count (value)) {
@@ -82,22 +111,6 @@ class PartitionOpTranslator {
82111 return nextNodeID++;
83112 }
84113
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-
101114 // check the passed type for sendability, special casing the type used for
102115 // raw pointers to ensure it is treated as non-Sendable and strict checking
103116 // is applied to it
@@ -116,6 +129,7 @@ class PartitionOpTranslator {
116129 // to be functions or class methods because these can safely be treated as
117130 // Sendable despite not having true Sendable type
118131 bool isNonSendable (SILValue value) {
132+ value = simplifyVal (value);
119133 SILInstruction *defInst = value.getDefiningInstruction ();
120134 if (defInst && isa<ClassMethodInst, FunctionRefInst>(defInst)) {
121135 // though these values are technically non-Sendable, we can safely
@@ -135,37 +149,46 @@ class PartitionOpTranslator {
135149 // The following section of functions create fresh PartitionOps referencing
136150 // the current value of currentInstruction for ease of programming.
137151
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)} ;
141155 }
142156
143- PartitionOp Assign (SILValue tgt, SILValue src) {
157+ std::vector< PartitionOp> Assign (SILValue tgt, SILValue src) {
144158 assert (nodeHasID (src) &&
145159 " 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)};
148166 }
149167
150- PartitionOp Consume (SILValue value) {
168+ std::vector< PartitionOp> Consume (SILValue value) {
151169 assert (nodeHasID (value) &&
152170 " consumed value should already have been encountered" );
153- return PartitionOp::Consume (lookupNodeID (value),
154- currentInstruction);
171+
172+ return {PartitionOp::Consume (lookupNodeID (value),
173+ currentInstruction)};
155174 }
156175
157- PartitionOp Merge (SILValue fst, SILValue snd) {
176+ std::vector< PartitionOp> Merge (SILValue fst, SILValue snd) {
158177 assert (nodeHasID (fst) && nodeHasID (snd) &&
159178 " 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)};
162185 }
163186
164- PartitionOp Require (SILValue value) {
187+ std::vector< PartitionOp> Require (SILValue value) {
165188 assert (nodeHasID (value) &&
166189 " required value should already have been encountered" );
167- return PartitionOp::Require (lookupNodeID (value),
168- currentInstruction);
190+ return { PartitionOp::Require (lookupNodeID (value),
191+ currentInstruction)} ;
169192 }
170193 // ===========================================================================
171194
@@ -233,17 +256,20 @@ class PartitionOpTranslator {
233256 bool nonSendableResult = isNonSendable (applyInst->getResult (0 ));
234257
235258 std::vector<PartitionOp> translated;
259+ auto add_to_translation = [&](std::vector<PartitionOp> ops) {
260+ for (auto op : ops) translated.push_back (op);
261+ };
236262
237263 if (SILApplyCrossesIsolation (applyInst)) {
238264 // for calls that cross isolation domains, consume all operands
239265 for (SILValue operand : nonSendableOperands)
240- translated. push_back (Consume (operand));
266+ add_to_translation (Consume (operand));
241267
242268 if (nonSendableResult) {
243269 // returning non-Sendable values from a cross isolation call will always
244270 // be an error, but doesn't need to be diagnosed here, so let's pretend
245271 // it gets a fresh region
246- translated. push_back (AssignFresh (applyInst->getResult (0 )));
272+ add_to_translation (AssignFresh (applyInst->getResult (0 )));
247273 }
248274 return translated;
249275 }
@@ -254,83 +280,73 @@ class PartitionOpTranslator {
254280 if (nonSendableOperands.empty ()) {
255281 // if no operands, a non-Sendable result gets a fresh region
256282 if (nonSendableResult) {
257- translated. push_back (AssignFresh (applyInst->getResult (0 )));
283+ add_to_translation (AssignFresh (applyInst->getResult (0 )));
258284 }
259285 return translated;
260286 }
261287
262288 if (nonSendableOperands.size () == 1 ) {
263289 // only one operand, so no merges required; just a `Require`
264- translated. push_back (Require (nonSendableOperands.front ()));
290+ add_to_translation (Require (nonSendableOperands.front ()));
265291 } else {
266292 // merge all operands
267293 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 ),
269295 nonSendableOperands.at (i)));
270296 }
271297 }
272298
273299 // if the result is non-Sendable, assign it to the region of the operands
274300 if (nonSendableResult) {
275- translated. push_back (
301+ add_to_translation (
276302 Assign (applyInst->getResult (0 ), nonSendableOperands.front ()));
277303 }
278304
279305 return translated;
280306 }
281307
282- std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src,
283- bool projecting = false ) {
308+ std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src) {
284309 // no work to be done if assignment is to a Sendable target
285310 if (!isNonSendable (tgt))
286311 return {};
287312
288313 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);
307316 }
308317
309318 // a non-Sendable value is extracted from a Sendable value,
310319 // seems to only occur when performing unchecked casts like
311320 // `unchecked_ref_cast`
312- return { AssignFresh (tgt)} ;
321+ return AssignFresh (tgt);
313322 }
314323
315324 // If the passed SILValue is NonSendable, then create a fresh region for it,
316325 // otherwise do nothing.
317326 std::vector<PartitionOp> translateSILAssignFresh (SILValue fresh) {
318327 if (isNonSendable (fresh)) {
319- return { AssignFresh (fresh)} ;
328+ return AssignFresh (fresh);
320329 }
321330 return {};
322331 }
323332
324333 std::vector<PartitionOp> translateSILMerge (SILValue fst, SILValue snd) {
325334 if (isNonSendable (fst) && isNonSendable (snd)) {
326- return { Merge (fst, snd)} ;
335+ return Merge (fst, snd);
327336 }
328337 return {};
329338 }
330339
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+
331347 std::vector<PartitionOp> translateSILRequire (SILValue val) {
332348 if (isNonSendable (val)) {
333- return { Require (val)} ;
349+ return Require (val);
334350 }
335351 return {};
336352 }
@@ -359,10 +375,13 @@ class PartitionOpTranslator {
359375 // (e.g. CopyValueInst, LoadInst, IndexAddrInst) or because the operand
360376 // is unusable once the result is defined (e.g. the unchecked casts)
361377 if (isa<AddressToPointerInst,
378+ BeginAccessInst,
379+ BeginBorrowInst,
362380 CopyValueInst,
363381 ConvertEscapeToNoEscapeInst,
364382 ConvertFunctionInst,
365383 IndexAddrInst,
384+ InitExistentialAddrInst,
366385 LoadInst,
367386 LoadBorrowInst,
368387 LoadWeakInst,
@@ -378,28 +397,14 @@ class PartitionOpTranslator {
378397 instruction->getOperand (0 ));
379398 }
380399
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-
395400 // The following instructions are treated as non-projecting assignments,
396401 // but between their two operands instead of their operand and result.
397402 if (isa<CopyAddrInst,
398- ExplicitCopyAddrInst,
403+ ExplicitCopyAddrInst,
399404 StoreInst,
400405 StoreBorrowInst,
401406 StoreWeakInst>(instruction)) {
402- return translateSILAssign (
407+ return translateSILStore (
403408 instruction->getOperand (1 ),
404409 instruction->getOperand (0 ));
405410 }
0 commit comments