mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[allocbox-to-stack] When we promote a heap -> stack of noncopyable type, move mark_must_check onto alloc_stack and always
ensure that we use consumable_to_assign. What this patch is does is add an extra phase after alloc_box runs where we look at uses of the alloc_stack and if we see any mark_must_check of any kind, we delete them and rewrite a single mark_must_check [consumable_and_assignable] on the alloc_stack and make all uses of the alloc_stack go through the mark_must_check. This has two effects: 1. In a subsequent PR when I add noncopyable semantics for escaping closures, this will cause allocbox to stack to convert such boxes from having escaping semantics to having non-escaping semantics. Escaping semantics means that we always reproject out from the box and use mark_must_check [assignable_but_not_consumable] (since we can't consume from the box, but can assign to it). In contrast, non-escaping semantics means that the box becomes an alloc_stack and we use the traditional var checker semantics. NOTE: We can do this for lets represented as addresses and vars since the typechecker will validate that the let is never actually written to even if at the SIL level we would allow that. 2. In cases where we are implementing simple mark_must_check [consumable_and_assignable] on one of the project_box and capture the box, we used to have a problem where the direct box uses would be on the alloc_stack and not go through the mark_must_check. Now, all uses after allocbox_to_stack occur go through the mark_must_check. This is why I was able to remove instances of the "compiler does not understand this pattern" errors... since the compiler with this change can now understand them.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "swift/SIL/ApplySite.h"
|
||||
#include "swift/SIL/BasicBlockDatastructures.h"
|
||||
#include "swift/SIL/Dominance.h"
|
||||
#include "swift/SIL/MemAccessUtils.h"
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
#include "swift/SIL/SILBuilder.h"
|
||||
#include "swift/SIL/SILCloner.h"
|
||||
@@ -500,25 +501,70 @@ struct AllocBoxToStackState {
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
static void replaceProjectBoxUsers(SILValue HeapBox, SILValue StackBox) {
|
||||
SmallVector<Operand *, 8> Worklist(HeapBox->use_begin(), HeapBox->use_end());
|
||||
while (!Worklist.empty()) {
|
||||
auto *Op = Worklist.pop_back_val();
|
||||
if (auto *PBI = dyn_cast<ProjectBoxInst>(Op->getUser())) {
|
||||
static void replaceProjectBoxUsers(SILValue heapBox, SILValue stackBox) {
|
||||
StackList<Operand *> worklist(heapBox->getFunction());
|
||||
for (auto *use : heapBox->getUses())
|
||||
worklist.push_back(use);
|
||||
while (!worklist.empty()) {
|
||||
auto *nextUse = worklist.pop_back_val();
|
||||
if (auto *pbi = dyn_cast<ProjectBoxInst>(nextUse->getUser())) {
|
||||
// This may result in an alloc_stack being used by begin_access [dynamic].
|
||||
PBI->replaceAllUsesWith(StackBox);
|
||||
pbi->replaceAllUsesWith(stackBox);
|
||||
pbi->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *User = Op->getUser();
|
||||
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User) ||
|
||||
isa<BeginBorrowInst>(User)) {
|
||||
llvm::copy(cast<SingleValueInstruction>(User)->getUses(),
|
||||
std::back_inserter(Worklist));
|
||||
auto *user = nextUse->getUser();
|
||||
if (isa<MarkUninitializedInst>(user) || isa<CopyValueInst>(user) ||
|
||||
isa<BeginBorrowInst>(user)) {
|
||||
for (auto *use : cast<SingleValueInstruction>(user)->getUses()) {
|
||||
worklist.push_back(use);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hoistMarkMustCheckInsts(SingleValueInstruction *stackBox) {
|
||||
StackList<Operand *> worklist(stackBox->getFunction());
|
||||
|
||||
for (auto *use : stackBox->getUses()) {
|
||||
worklist.push_back(use);
|
||||
}
|
||||
|
||||
bool foundTarget = false;
|
||||
while (!worklist.empty()) {
|
||||
auto *nextUse = worklist.pop_back_val();
|
||||
auto *nextUser = nextUse->getUser();
|
||||
|
||||
if (isa<BeginBorrowInst>(nextUser) || isa<BeginAccessInst>(nextUser) ||
|
||||
isa<CopyValueInst>(nextUser) || isa<MarkUninitializedInst>(nextUser)) {
|
||||
for (auto result : nextUser->getResults()) {
|
||||
for (auto *use : result->getUses())
|
||||
worklist.push_back(use);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *mmci = dyn_cast<MarkMustCheckInst>(nextUser)) {
|
||||
foundTarget = true;
|
||||
mmci->replaceAllUsesWith(mmci->getOperand());
|
||||
mmci->eraseFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundTarget)
|
||||
return;
|
||||
|
||||
SILBuilderWithScope builder(stackBox);
|
||||
builder.insertAfter(stackBox, [&](SILBuilder &b) {
|
||||
auto *undef = SILUndef::get(stackBox->getType(), stackBox->getModule());
|
||||
auto *mmci = b.createMarkMustCheckInst(
|
||||
stackBox->getLoc(), undef,
|
||||
MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
|
||||
stackBox->replaceAllUsesWith(mmci);
|
||||
mmci->setOperand(stackBox);
|
||||
});
|
||||
}
|
||||
|
||||
/// rewriteAllocBoxAsAllocStack - Replace uses of the alloc_box with a
|
||||
/// new alloc_stack, but do not delete the alloc_box yet.
|
||||
static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
|
||||
@@ -574,7 +620,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
|
||||
ABI->hasDynamicLifetime(), isLexical());
|
||||
|
||||
// Transfer a mark_uninitialized if we have one.
|
||||
SILValue StackBox = ASI;
|
||||
SingleValueInstruction *StackBox = ASI;
|
||||
if (Kind) {
|
||||
StackBox =
|
||||
Builder.createMarkUninitialized(ASI->getLoc(), ASI, Kind.value());
|
||||
@@ -584,6 +630,12 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
|
||||
// the address of the stack location.
|
||||
replaceProjectBoxUsers(HeapBox, StackBox);
|
||||
|
||||
// Then hoist any mark_must_check [assignable_but_not_consumable] to the
|
||||
// alloc_stack and convert them to [consumable_but_not_assignable]. This is
|
||||
// because we are semantically converting from escaping semantics to
|
||||
// non-escaping semantics.
|
||||
hoistMarkMustCheckInsts(StackBox);
|
||||
|
||||
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
|
||||
&& "promoting multi-field box not implemented");
|
||||
auto &Lowering = ABI->getFunction()->getTypeLowering(
|
||||
|
||||
Reference in New Issue
Block a user