Skip to content

Commit f2b5b86

Browse files
committed
[region-isolation] Learn how to find variable names around phi arguments.
With this, the only remaining "task or actor isolated" error is due to changes I need to land around async let.
1 parent ec81ecd commit f2b5b86

File tree

6 files changed

+303
-15
lines changed

6 files changed

+303
-15
lines changed

include/swift/SILOptimizer/Utils/VariableNameUtils.h

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_SILOPTIMIZER_UTILS_VARIABLENAMEUTILS_H
1818
#define SWIFT_SILOPTIMIZER_UTILS_VARIABLENAMEUTILS_H
1919

20+
#include "swift/Basic/Defer.h"
2021
#include "swift/Basic/OptionSet.h"
2122
#include "swift/SIL/ApplySite.h"
2223
#include "swift/SIL/DebugUtils.h"
@@ -40,8 +41,76 @@ class VariableNameInferrer {
4041
using Options = OptionSet<Flag>;
4142

4243
private:
43-
/// The stacklist that we use to process from use->
44-
StackList<PointerUnion<SILInstruction *, SILValue>> variableNamePath;
44+
template <typename T, unsigned smallSize>
45+
class VariableNamePathArray {
46+
SmallVector<T, smallSize> data;
47+
48+
unsigned lastSnapShotIndex = 0;
49+
unsigned insertionPointIndex = 0;
50+
51+
public:
52+
VariableNamePathArray() : data() {}
53+
54+
ArrayRef<T> getData() const {
55+
assert(insertionPointIndex <= data.size());
56+
return ArrayRef<T>(data).take_front(insertionPointIndex);
57+
}
58+
59+
void print(llvm::raw_ostream &os) const {
60+
os << "LastSnapShotIndex: " << lastSnapShotIndex << '\n';
61+
os << "InsertionPointIndex: " << insertionPointIndex << '\n';
62+
}
63+
64+
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
65+
66+
/// Pushes a snapshot and returns the old index.
67+
unsigned pushSnapShot() & {
68+
// After we run with a snapshot, we want:
69+
//
70+
// 1. lastSnapShotIndex to return to its value before the continuation
71+
// ran.
72+
// 2. The insertion point index becomes lastSnapShotIndex.
73+
unsigned oldSnapShotIndex = lastSnapShotIndex;
74+
lastSnapShotIndex = insertionPointIndex;
75+
return oldSnapShotIndex;
76+
}
77+
78+
void popSnapShot(unsigned oldIndex) & {
79+
insertionPointIndex = lastSnapShotIndex;
80+
lastSnapShotIndex = oldIndex;
81+
}
82+
83+
void returnSnapShot(unsigned oldIndex) & { lastSnapShotIndex = oldIndex; }
84+
85+
void push_back(const T &newValue) & {
86+
SWIFT_DEFER { assert(insertionPointIndex <= data.size()); };
87+
if (insertionPointIndex == data.size()) {
88+
data.push_back(newValue);
89+
++insertionPointIndex;
90+
return;
91+
}
92+
93+
data[insertionPointIndex] = newValue;
94+
++insertionPointIndex;
95+
}
96+
97+
[[nodiscard]] T pop_back_val() & {
98+
SWIFT_DEFER { assert(insertionPointIndex <= data.size()); };
99+
assert(!lastSnapShotIndex &&
100+
"Can only pop while lastSnapShotIndex is not set");
101+
--insertionPointIndex;
102+
return data[insertionPointIndex];
103+
}
104+
105+
bool empty() const { return !insertionPointIndex; }
106+
};
107+
108+
/// The stacklist that we use to print out variable names.
109+
///
110+
/// Has to be a small vector since we push/pop the last segment start. This
111+
/// lets us speculate when processing phis.
112+
VariableNamePathArray<PointerUnion<SILInstruction *, SILValue>, 4>
113+
variableNamePath;
45114

46115
/// The root value of our string.
47116
///
@@ -59,12 +128,12 @@ class VariableNameInferrer {
59128

60129
public:
61130
VariableNameInferrer(SILFunction *fn, SmallString<64> &resultingString)
62-
: variableNamePath(fn), resultingString(resultingString) {}
131+
: variableNamePath(), resultingString(resultingString) {}
63132

64133
VariableNameInferrer(SILFunction *fn, Options options,
65134
SmallString<64> &resultingString)
66-
: variableNamePath(fn), resultingString(resultingString),
67-
options(options) {}
135+
: variableNamePath(), resultingString(resultingString), options(options) {
136+
}
68137

69138
/// Attempts to infer a name from just uses of \p searchValue.
70139
///
@@ -133,7 +202,12 @@ class VariableNameInferrer {
133202

134203
/// Do not call this directly. Used just to improve logging for
135204
/// findDebugInfoProvidingValue.
136-
SILValue findDebugInfoProvidingValueHelper(SILValue searchValue);
205+
SILValue findDebugInfoProvidingValueHelper(SILValue searchValue,
206+
ValueSet &visitedValues);
207+
208+
/// A special helper for handling phi values. Do not call this directly.
209+
SILValue findDebugInfoProvidingValuePhiArg(SILValue incomingValue,
210+
ValueSet &visitedValues);
137211

138212
/// Given an initialized once allocation inst without a ValueDecl or a
139213
/// DebugVariable provided name, attempt to find a root value from its

lib/SILOptimizer/Utils/VariableNameUtils.cpp

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ VariableNameInferrer::findDebugInfoProvidingValue(SILValue searchValue) {
214214
return SILValue();
215215
LLVM_DEBUG(llvm::dbgs() << "Searching for debug info providing value for: "
216216
<< searchValue);
217-
SILValue result = findDebugInfoProvidingValueHelper(searchValue);
217+
ValueSet valueSet(searchValue->getFunction());
218+
SILValue result = findDebugInfoProvidingValueHelper(searchValue, valueSet);
218219
if (result) {
219220
LLVM_DEBUG(llvm::dbgs() << "Result: " << result);
220221
} else {
@@ -223,12 +224,45 @@ VariableNameInferrer::findDebugInfoProvidingValue(SILValue searchValue) {
223224
return result;
224225
}
225226

226-
SILValue
227-
VariableNameInferrer::findDebugInfoProvidingValueHelper(SILValue searchValue) {
227+
SILValue VariableNameInferrer::findDebugInfoProvidingValuePhiArg(
228+
SILValue incomingValue, ValueSet &visitedValues) {
229+
// We use pushSnapShot to run recursively and if we fail to find a
230+
// value, we just pop our list to the last snapshot end of list. If we
231+
// succeed, we do not pop and just return recusive value. Our user
232+
// will consume variableNamePath at this point.
233+
LLVM_DEBUG(llvm::dbgs() << "Before pushing a snap shot!\n";
234+
variableNamePath.print(llvm::dbgs()));
235+
236+
unsigned oldSnapShotIndex = variableNamePath.pushSnapShot();
237+
LLVM_DEBUG(llvm::dbgs() << "After pushing a snap shot!\n";
238+
variableNamePath.print(llvm::dbgs()));
239+
240+
if (SILValue recursiveValue =
241+
findDebugInfoProvidingValueHelper(incomingValue, visitedValues)) {
242+
LLVM_DEBUG(llvm::dbgs() << "Returned: " << recursiveValue);
243+
variableNamePath.returnSnapShot(oldSnapShotIndex);
244+
return recursiveValue;
245+
}
246+
247+
variableNamePath.popSnapShot(oldSnapShotIndex);
248+
LLVM_DEBUG(llvm::dbgs() << "After popping a snap shot!\n";
249+
variableNamePath.print(llvm::dbgs()));
250+
return SILValue();
251+
}
252+
253+
SILValue VariableNameInferrer::findDebugInfoProvidingValueHelper(
254+
SILValue searchValue, ValueSet &visitedValues) {
228255
assert(searchValue);
229256

230257
while (true) {
231258
assert(searchValue);
259+
260+
// If we already visited the value, return SILValue(). This prevents issues
261+
// caused by looping phis. We treat this as a failure and visit the either
262+
// phi values.
263+
if (!visitedValues.insert(searchValue))
264+
return SILValue();
265+
232266
LLVM_DEBUG(llvm::dbgs() << "Value: " << *searchValue);
233267

234268
if (auto *allocInst = dyn_cast<AllocationInst>(searchValue)) {
@@ -306,6 +340,17 @@ VariableNameInferrer::findDebugInfoProvidingValueHelper(SILValue searchValue) {
306340
continue;
307341
}
308342

343+
// Enums only have a single possible parent and is used sometimes like a
344+
// transformation (e.x.: constructing an optional). We want to look through
345+
// them and add the case to the variableNamePath.
346+
if (auto *e = dyn_cast<EnumInst>(searchValue)) {
347+
if (e->hasOperand()) {
348+
variableNamePath.push_back(e);
349+
searchValue = e->getOperand();
350+
continue;
351+
}
352+
}
353+
309354
if (auto *dti = dyn_cast_or_null<DestructureTupleInst>(
310355
searchValue->getDefiningInstruction())) {
311356
// Append searchValue, so we can find the specific tuple index.
@@ -329,6 +374,27 @@ VariableNameInferrer::findDebugInfoProvidingValueHelper(SILValue searchValue) {
329374
}
330375
}
331376

377+
// If we have a phi argument, visit each of the incoming values and pick the
378+
// first one that gives us a name.
379+
if (auto *phiArg = dyn_cast<SILPhiArgument>(searchValue)) {
380+
if (auto *term = phiArg->getSingleTerminator()) {
381+
if (auto *swi = dyn_cast<SwitchEnumInst>(term)) {
382+
if (auto value = findDebugInfoProvidingValuePhiArg(swi->getOperand(),
383+
visitedValues))
384+
return value;
385+
}
386+
}
387+
388+
SmallVector<SILValue, 8> incomingValues;
389+
if (phiArg->getIncomingPhiValues(incomingValues)) {
390+
for (auto value : incomingValues) {
391+
if (auto resultValue =
392+
findDebugInfoProvidingValuePhiArg(value, visitedValues))
393+
return resultValue;
394+
}
395+
}
396+
}
397+
332398
auto getNamePathComponentFromCallee = [&](FullApplySite call) -> SILValue {
333399
// Use the name of the property being accessed if we can get to it.
334400
if (isa<FunctionRefBaseInst>(call.getCallee()) ||
@@ -506,6 +572,11 @@ void VariableNameInferrer::popSingleVariableName() {
506572
return;
507573
}
508574

575+
if (auto *ei = dyn_cast<EnumInst>(inst)) {
576+
resultingString += getNameFromDecl(ei->getElement());
577+
return;
578+
}
579+
509580
resultingString += "<unknown decl>";
510581
return;
511582
}

test/Concurrency/transfernonsendable.sil

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ bb2(%1 : @guaranteed $NonSendableKlass):
277277
br bb3(%3 : $FakeOptional<NonSendableKlass>)
278278

279279
bb3(%4 : @owned $FakeOptional<NonSendableKlass>):
280-
return %4 : $FakeOptional<NonSendableKlass> // expected-warning {{task or actor isolated value cannot be transferred}}
280+
return %4 : $FakeOptional<NonSendableKlass> // expected-warning {{transferring 'myname.some' may cause a race}}
281+
// expected-note @-1 {{main actor-isolated 'myname.some' cannot be a transferring result. main actor-isolated uses may race with caller uses}}
281282
}
282283

283284
sil [ossa] @warningIfCallingGetter : $@convention(method) @async (@sil_isolated @guaranteed MyActor) -> () {

test/Concurrency/transfernonsendable_strong_transferring_results.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ extension MainActorIsolatedEnum {
153153
case .second(let ns):
154154
return ns
155155
}
156-
// TODO: This should be on return ns.
157-
} // expected-warning {{task or actor isolated value cannot be transferred}}
156+
} // expected-warning {{transferring 'ns.some' may cause a race}}
157+
// expected-note @-1 {{main actor-isolated 'ns.some' cannot be a transferring result. main actor-isolated uses may race with caller uses}}
158158

159159
func testSwitchReturnNoTransfer() -> NonSendableKlass? {
160160
switch self {
@@ -170,7 +170,8 @@ extension MainActorIsolatedEnum {
170170
return ns // TODO: The error below should be here.
171171
}
172172
return nil
173-
} // expected-warning {{task or actor isolated value cannot be transferred}}
173+
} // expected-warning {{transferring 'ns.some' may cause a race}}
174+
// expected-note @-1 {{main actor-isolated 'ns.some' cannot be a transferring result. main actor-isolated uses may race with caller uses}}
174175

175176
func testIfLetReturnNoTransfer() -> NonSendableKlass? {
176177
if case .second(let ns) = self {

test/SILOptimizer/moveonly_borrowing_switch.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,15 +214,15 @@ func testOuterAO(consuming bas: consuming AOBas) { // expected-error{{'bas' used
214214
where condition(x):
215215
nibble(payload: x)
216216
eat(payload: x)
217-
case .payload(let x) // expected-error {{'unknown' is borrowed and cannot be consumed}}
217+
case .payload(let x) // expected-error {{'bas.payload' is borrowed and cannot be consumed}}
218218
where hungryCondition(x): // expected-note {{consumed here}}
219219
nibble(payload: x)
220220
eat(payload: x)
221221
case .payload(.loadablePayload(let x))
222222
where condition(x):
223223
nibble(payload: x)
224224
eat(payload: x)
225-
case .payload(.loadablePayload(.payload(let x))) // expected-error{{'unknown' is borrowed and cannot be consumed}}
225+
case .payload(.loadablePayload(.payload(let x))) // expected-error{{'bas.payload.loadablePayload' is borrowed and cannot be consumed}}
226226
where hungryCondition(x): // expected-note{{consumed here}}
227227
nibble(payload: x)
228228
eat(payload: x)

0 commit comments

Comments
 (0)