Skip to content

Commit 1a716d8

Browse files
author
jturcotti
committed
replace use of internal "projections" tracking map with usage of memutils' AccessStorage utilities
1 parent c3af373 commit 1a716d8

File tree

2 files changed

+91
-88
lines changed

2 files changed

+91
-88
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,19 @@ class PartitionOp {
8888
void dump() const LLVM_ATTRIBUTE_USED {
8989
switch (OpKind) {
9090
case PartitionOpKind::Assign:
91-
llvm::dbgs() << "assign %" << OpArgs[0] << " = %" << OpArgs[1] << "\n";
91+
llvm::dbgs() << "assign %%" << OpArgs[0] << " = %%" << OpArgs[1] << "\n";
9292
break;
9393
case PartitionOpKind::AssignFresh:
94-
llvm::dbgs() << "assign_fresh %" << OpArgs[0] << "\n";
94+
llvm::dbgs() << "assign_fresh %%" << OpArgs[0] << "\n";
9595
break;
9696
case PartitionOpKind::Consume:
97-
llvm::dbgs() << "consume %" << OpArgs[0] << "\n";
97+
llvm::dbgs() << "consume %%" << OpArgs[0] << "\n";
9898
break;
9999
case PartitionOpKind::Merge:
100-
llvm::dbgs() << "merge %" << OpArgs[0] << " with %" << OpArgs[1] << "\n";
100+
llvm::dbgs() << "merge %%" << OpArgs[0] << " with %%" << OpArgs[1] << "\n";
101101
break;
102102
case PartitionOpKind::Require:
103-
llvm::dbgs() << "require %" << OpArgs[0] << "\n";
103+
llvm::dbgs() << "require %%" << OpArgs[0] << "\n";
104104
break;
105105
}
106106
}
@@ -406,9 +406,7 @@ class Partition {
406406
}
407407
llvm::dbgs() << (label < 0 ? "}" : ")");
408408
}
409-
llvm::dbgs() << "] | ";
410-
411-
dump_labels();
409+
llvm::dbgs() << "]";
412410
}
413411
};
414412
}

lib/SILOptimizer/Mandatory/SendNonSendable.cpp

Lines changed: 85 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
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

Comments
 (0)