Skip to content

Commit b5c08e9

Browse files
authored
Merge pull request #7471 from gottesmm/writebackscope
2 parents 8af3196 + 9747214 commit b5c08e9

12 files changed

+358
-239
lines changed

lib/SILGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_swift_library(swiftSILGen STATIC
22
ArgumentSource.cpp
33
Cleanup.cpp
44
Condition.cpp
5+
FormalEvaluation.cpp
56
ManagedValue.cpp
67
RValue.cpp
78
SILGen.cpp

lib/SILGen/FormalEvaluation.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===--- FormalEvaluation.cpp ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "FormalEvaluation.h"
14+
#include "LValue.h"
15+
#include "SILGenFunction.h"
16+
17+
using namespace swift;
18+
using namespace Lowering;
19+
20+
//===----------------------------------------------------------------------===//
21+
// Formal Evaluation
22+
//===----------------------------------------------------------------------===//
23+
24+
void FormalEvaluation::_anchor() {}
25+
26+
//===----------------------------------------------------------------------===//
27+
// Formal Evaluation Scope
28+
//===----------------------------------------------------------------------===//
29+
30+
FormalEvaluationScope::FormalEvaluationScope(SILGenFunction &gen)
31+
: gen(gen), savedDepth(gen.FormalEvalContext.stable_begin()),
32+
wasInWritebackScope(gen.InWritebackScope) {
33+
if (gen.InInOutConversionScope) {
34+
savedDepth.reset();
35+
return;
36+
}
37+
gen.InWritebackScope = true;
38+
}
39+
40+
FormalEvaluationScope::FormalEvaluationScope(FormalEvaluationScope &&o)
41+
: gen(o.gen), savedDepth(o.savedDepth),
42+
wasInWritebackScope(o.wasInWritebackScope) {
43+
o.savedDepth.reset();
44+
}
45+
46+
void FormalEvaluationScope::popImpl() {
47+
// Pop the InWritebackScope bit.
48+
gen.InWritebackScope = wasInWritebackScope;
49+
50+
// Check to see if there is anything going on here.
51+
52+
auto &context = gen.FormalEvalContext;
53+
using iterator = FormalEvaluationContext::iterator;
54+
using stable_iterator = FormalEvaluationContext::stable_iterator;
55+
56+
iterator unwrappedSavedDepth = context.find(savedDepth.getValue());
57+
iterator iter = context.begin();
58+
if (iter == unwrappedSavedDepth)
59+
return;
60+
61+
// Save our start point to make sure that we are not adding any new cleanups
62+
// to the front of the stack.
63+
stable_iterator originalBegin = context.stable_begin();
64+
65+
// Then working down the stack until we visit unwrappedSavedDepth...
66+
for (; iter != unwrappedSavedDepth; ++iter) {
67+
// Grab the next evaluation...
68+
FormalEvaluation &evaluation = *iter;
69+
70+
// and deactivate the cleanup.
71+
gen.Cleanups.setCleanupState(evaluation.getCleanup(), CleanupState::Dead);
72+
73+
// Attempt to diagnose problems where obvious aliasing introduces illegal
74+
// code. We do a simple N^2 comparison here to detect this because it is
75+
// extremely unlikely more than a few writebacks are active at once.
76+
if (evaluation.getKind() == FormalEvaluation::Exclusive) {
77+
iterator j = iter;
78+
++j;
79+
80+
for (; j != unwrappedSavedDepth; ++j) {
81+
FormalEvaluation &other = *j;
82+
if (other.getKind() != FormalEvaluation::Exclusive)
83+
continue;
84+
auto &lhs = static_cast<LValueWriteback &>(evaluation);
85+
auto &rhs = static_cast<LValueWriteback &>(other);
86+
lhs.diagnoseConflict(rhs, gen);
87+
}
88+
}
89+
90+
// Claim the address of each and then perform the writeback from the
91+
// temporary allocation to the source we copied from.
92+
//
93+
// This evaluates arbitrary code, so it's best to be paranoid
94+
// about iterators on the context.
95+
evaluation.finish(gen);
96+
}
97+
98+
// Then check that we did not add any additional cleanups to the beginning of
99+
// the stack...
100+
assert(originalBegin == context.stable_begin() &&
101+
"more writebacks placed onto context during writeback scope pop?!");
102+
103+
// And then pop off all stack elements until we reach the savedDepth.
104+
context.pop(savedDepth.getValue());
105+
}

lib/SILGen/FormalEvaluation.h

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//===--- FormalEvaluation.h -----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SILGEN_FORMALEVALUATION_H
14+
#define SWIFT_SILGEN_FORMALEVALUATION_H
15+
16+
#include "Cleanup.h"
17+
#include "swift/Basic/DiverseStack.h"
18+
#include "llvm/ADT/Optional.h"
19+
20+
namespace swift {
21+
namespace Lowering {
22+
23+
class SILGenFunction;
24+
class LogicalPathComponent;
25+
26+
class FormalEvaluation {
27+
public:
28+
enum Kind { Shared, Exclusive };
29+
30+
private:
31+
unsigned allocatedSize;
32+
Kind kind;
33+
34+
protected:
35+
SILLocation loc;
36+
CleanupHandle cleanup;
37+
38+
FormalEvaluation(unsigned allocatedSize, Kind kind, SILLocation loc,
39+
CleanupHandle cleanup)
40+
: allocatedSize(allocatedSize), kind(kind), loc(loc), cleanup(cleanup) {}
41+
42+
public:
43+
virtual ~FormalEvaluation() {}
44+
45+
// This anchor method serves three purposes: it aligns the class to
46+
// a pointer boundary, it makes the class a primary base so that
47+
// subclasses will be at offset zero, and it anchors the v-table
48+
// to a specific file.
49+
virtual void _anchor();
50+
51+
/// Return the allocated size of this object. This is required by
52+
/// DiverseStack for iteration.
53+
size_t allocated_size() const { return allocatedSize; }
54+
55+
CleanupHandle getCleanup() const { return cleanup; }
56+
57+
Kind getKind() const { return kind; }
58+
59+
virtual void finish(SILGenFunction &gen) = 0;
60+
};
61+
62+
class FormalEvaluationContext {
63+
DiverseStack<FormalEvaluation, 128> stack;
64+
65+
public:
66+
using stable_iterator = decltype(stack)::stable_iterator;
67+
using iterator = decltype(stack)::iterator;
68+
69+
FormalEvaluationContext() : stack() {}
70+
71+
// This is a type that can only be embedded in other types, it can not be
72+
// moved or copied.
73+
FormalEvaluationContext(const FormalEvaluationContext &) = delete;
74+
FormalEvaluationContext(FormalEvaluationContext &&) = delete;
75+
FormalEvaluationContext &operator=(const FormalEvaluationContext &) = delete;
76+
FormalEvaluationContext &operator=(FormalEvaluationContext &&) = delete;
77+
78+
~FormalEvaluationContext() {
79+
assert(stack.empty() &&
80+
"entries remaining on writeback stack at end of function!");
81+
}
82+
83+
iterator begin() { return stack.begin(); }
84+
iterator end() { return stack.end(); }
85+
stable_iterator stabilize(iterator iter) const {
86+
return stack.stabilize(iter);
87+
}
88+
stable_iterator stable_begin() { return stabilize(begin()); }
89+
iterator find(stable_iterator iter) { return stack.find(iter); }
90+
91+
template <class U, class... ArgTypes> void push(ArgTypes &&... args) {
92+
stack.push<U>(std::forward<ArgTypes>(args)...);
93+
}
94+
95+
void pop() { stack.pop(); }
96+
97+
/// Pop objects off of the stack until \p the object pointed to by stable_iter
98+
/// is the top element of the stack.
99+
void pop(stable_iterator stable_iter) { stack.pop(stable_iter); }
100+
};
101+
102+
class FormalEvaluationScope {
103+
SILGenFunction &gen;
104+
llvm::Optional<FormalEvaluationContext::stable_iterator> savedDepth;
105+
bool wasInWritebackScope;
106+
107+
public:
108+
FormalEvaluationScope(SILGenFunction &gen);
109+
~FormalEvaluationScope() {
110+
if (!savedDepth.hasValue())
111+
return;
112+
popImpl();
113+
}
114+
115+
bool isPopped() const { return !savedDepth.hasValue(); }
116+
117+
void pop() {
118+
assert(!isPopped() && "popping an already-popped writeback scope!");
119+
popImpl();
120+
savedDepth.reset();
121+
}
122+
123+
FormalEvaluationScope(const FormalEvaluationScope &) = delete;
124+
FormalEvaluationScope &operator=(const FormalEvaluationScope &) = delete;
125+
126+
FormalEvaluationScope(FormalEvaluationScope &&o);
127+
FormalEvaluationScope &operator=(FormalEvaluationScope &&o) = delete;
128+
129+
private:
130+
void popImpl();
131+
};
132+
133+
} // namespace Lowering
134+
} // namespace swift
135+
136+
#endif

lib/SILGen/LValue.h

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@
2121
#ifndef SWIFT_LOWERING_LVALUE_H
2222
#define SWIFT_LOWERING_LVALUE_H
2323

24+
#include "FormalEvaluation.h"
2425
#include "SILGenFunction.h"
26+
#include "Scope.h"
2527

2628
namespace swift {
2729
namespace Lowering {
28-
class SILGenFunction;
29-
class ManagedValue;
3030

31-
class PhysicalPathComponent;
3231
class LogicalPathComponent;
32+
class ManagedValue;
33+
class PhysicalPathComponent;
34+
class SILGenFunction;
3335
class TranslationPathComponent;
3436

3537
/// Information about the type of an l-value.
@@ -455,52 +457,6 @@ class LValue {
455457
void print(raw_ostream &OS) const;
456458
};
457459

458-
/// RAII object to enable writebacks for logical lvalues evaluated within the
459-
/// scope, which will be applied when the object goes out of scope.
460-
///
461-
/// A writeback scope is used to limit the extent of a formal access
462-
/// to an l-value, under the rules specified in the accessors
463-
/// proposal. It should be entered at a point where it will conclude
464-
/// at the appropriate instant.
465-
///
466-
/// For example, the rules specify that a formal access for an inout
467-
/// argument begins immediately before the call and ends immediately
468-
/// after it. This can be implemented by pushing a WritebackScope
469-
/// before the formal evaluation of the arguments and popping it
470-
/// immediately after the call. (It must be pushed before the formal
471-
/// evaluation because, in some cases, the formal evaluation of a base
472-
/// l-value will immediately begin a formal access that must end at
473-
/// the same time as that of its projected subobject l-value.)
474-
class WritebackScope {
475-
SILGenFunction *gen;
476-
bool wasInWritebackScope;
477-
size_t savedDepth;
478-
void popImpl();
479-
public:
480-
WritebackScope(SILGenFunction &gen);
481-
~WritebackScope() {
482-
if (gen) {
483-
popImpl();
484-
}
485-
}
486-
487-
bool isPopped() const {
488-
return (gen == nullptr);
489-
}
490-
491-
void pop() {
492-
assert(!isPopped() && "popping an already-popped writeback scope!");
493-
popImpl();
494-
gen = nullptr;
495-
}
496-
497-
WritebackScope(const WritebackScope &) = delete;
498-
WritebackScope &operator=(const WritebackScope &) = delete;
499-
500-
WritebackScope(WritebackScope &&o);
501-
WritebackScope &operator=(WritebackScope &&o);
502-
};
503-
504460
/// RAII object used to enter an inout conversion scope. Writeback scopes formed
505461
/// during the inout conversion scope will be no-ops.
506462
class InOutConversionScope {
@@ -510,7 +466,51 @@ class InOutConversionScope {
510466
~InOutConversionScope();
511467
};
512468

513-
} // end namespace Lowering
514-
} // end namespace swift
469+
struct LLVM_LIBRARY_VISIBILITY LValueWriteback : FormalEvaluation {
470+
std::unique_ptr<LogicalPathComponent> component;
471+
ManagedValue base;
472+
MaterializedLValue materialized;
473+
474+
~LValueWriteback() {}
475+
LValueWriteback(LValueWriteback &&) = default;
476+
LValueWriteback &operator=(LValueWriteback &&) = default;
477+
478+
LValueWriteback() = default;
479+
LValueWriteback(SILLocation loc, std::unique_ptr<LogicalPathComponent> &&comp,
480+
ManagedValue base, MaterializedLValue materialized,
481+
CleanupHandle cleanup)
482+
: FormalEvaluation(sizeof(*this), FormalEvaluation::Exclusive, loc,
483+
cleanup),
484+
component(std::move(comp)), base(base), materialized(materialized) {}
485+
486+
void diagnoseConflict(const LValueWriteback &rhs, SILGenFunction &SGF) const {
487+
// If the two writebacks we're comparing are of different kinds (e.g.
488+
// ownership conversion vs a computed property) then they aren't the
489+
// same and thus cannot conflict.
490+
if (component->getKind() != rhs.component->getKind())
491+
return;
492+
493+
// If the lvalues don't have the same base value, then they aren't the same.
494+
// Note that this is the primary source of false negative for this
495+
// diagnostic.
496+
if (base.getValue() != rhs.base.getValue())
497+
return;
498+
499+
component->diagnoseWritebackConflict(rhs.component.get(), loc, rhs.loc,
500+
SGF);
501+
}
502+
503+
void performWriteback(SILGenFunction &gen, bool isFinal) {
504+
Scope S(gen.Cleanups, CleanupLocation::get(loc));
505+
component->writeback(gen, loc, base, materialized, isFinal);
506+
}
507+
508+
void finish(SILGenFunction &gen) override {
509+
performWriteback(gen, /*isFinal*/ true);
510+
}
511+
};
512+
513+
} // namespace Lowering
514+
} // namespace swift
515515

516516
#endif

0 commit comments

Comments
 (0)