Skip to content

[silgenpattern] NFC cleanups in preparation for fixing fallthrough issue #23132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 155 additions & 140 deletions lib/SILGen/SILGenPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2320,19 +2320,21 @@ void PatternMatchEmission::initSharedCaseBlockDest(CaseStmt *caseBlock,
result.first->second.first = block;

// Add args for any pattern variables
if (caseBlock->hasBoundDecls()) {
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
pattern->forEachVariable([&](VarDecl *V) {
if (!V->hasName())
return;

// We don't pass address-only values in basic block arguments.
SILType ty = SGF.getLoweredType(V->getType());
if (ty.isAddressOnly(SGF.F.getModule()))
return;
block->createPhiArgument(ty, ValueOwnershipKind::Owned, V);
});
if (!caseBlock->hasBoundDecls()) {
return;
}

auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
pattern->forEachVariable([&](VarDecl *V) {
if (!V->hasName())
return;

// We don't pass address-only values in basic block arguments.
SILType ty = SGF.getLoweredType(V->getType());
if (ty.isAddressOnly(SGF.F.getModule()))
return;
block->createPhiArgument(ty, ValueOwnershipKind::Owned, V);
});
}

/// Retrieve the jump destination for a shared case block.
Expand Down Expand Up @@ -2403,7 +2405,7 @@ emitAddressOnlyInitialization(VarDecl *dest, SILValue value) {

/// Emit all the shared case statements.
void PatternMatchEmission::emitSharedCaseBlocks() {
for (auto &entry: SharedCases) {
for (auto &entry : SharedCases) {
CaseStmt *caseBlock = entry.first;
SILBasicBlock *caseBB = entry.second.first;
bool hasFallthroughTo = entry.second.second;
Expand Down Expand Up @@ -2437,64 +2439,73 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
// Then emit the case body into the caseBB.
SGF.B.setInsertionPoint(caseBB);
}


// Make sure that before/after we emit the case body we have emitted all
// cleanups we created within.
assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth);
SWIFT_DEFER { assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth); };

// If we do not have any bound decls, just emit the case body since we do
// not need to setup any var locs.
if (!caseBlock->hasBoundDecls()) {
emitCaseBody(caseBlock);
continue;
}

// If we have a shared case with bound decls, then the 0th pattern has the
// order of variables that are the incoming BB arguments. Setup the VarLocs
// to point to the incoming args and setup initialization so any args needing
// cleanup will get that as well.
if (caseBlock->hasBoundDecls()) {
Scope scope(SGF.Cleanups, CleanupLocation(caseBlock));
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
unsigned argIndex = 0;
pattern->forEachVariable([&](VarDecl *V) {
if (!V->hasName())
return;

SILType ty = SGF.getLoweredType(V->getType());

// Initialize mv at +1. We always pass values in at +1 for today into
// shared blocks.
ManagedValue mv;
if (ty.isAddressOnly(SGF.F.getModule())) {
// There's no basic block argument, since we don't allow basic blocks
// to have address arguments.
//
// Instead, we map the variable to a temporary alloc_stack in
// emitAddressOnlyAllocations(), and store into it at each
// predecessor block.
//
// There's nothing to do here, since the value should already have
// been initialized on entry.
auto found = Temporaries.find(V);
assert(found != Temporaries.end());
mv = SGF.emitManagedRValueWithCleanup(found->second);
} else {
SILValue arg = caseBB->getArgument(argIndex++);
assert(arg.getOwnershipKind() == ValueOwnershipKind::Owned ||
arg.getOwnershipKind() == ValueOwnershipKind::Any);
mv = SGF.emitManagedRValueWithCleanup(arg);
}
// to point to the incoming args and setup initialization so any args
// needing cleanup will get that as well.
Scope scope(SGF.Cleanups, CleanupLocation(caseBlock));
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
unsigned argIndex = 0;
pattern->forEachVariable([&](VarDecl *V) {
if (!V->hasName())
return;

if (V->isLet()) {
// Just emit a let and leave the cleanup alone.
SGF.VarLocs[V].value = mv.getValue();
} else {
// The pattern variables were all emitted as lets and one got passed in,
// now we finally alloc a box for the var and forward in the chosen value.
SGF.VarLocs.erase(V);
auto newVar = SGF.emitInitializationForVarDecl(V, V->isLet());
newVar->copyOrInitValueInto(SGF, V, mv, /*isInit*/ true);
newVar->finishInitialization(SGF);
}
});
emitCaseBody(caseBlock);
} else {
emitCaseBody(caseBlock);
}

assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth);
SILType ty = SGF.getLoweredType(V->getType());

// Initialize mv at +1. We always pass values in at +1 for today into
// shared blocks.
ManagedValue mv;
if (ty.isAddressOnly(SGF.F.getModule())) {
// There's no basic block argument, since we don't allow basic blocks
// to have address arguments.
//
// Instead, we map the variable to a temporary alloc_stack in
// emitAddressOnlyAllocations(), and store into it at each
// predecessor block.
//
// There's nothing to do here, since the value should already have
// been initialized on entry.
auto found = Temporaries.find(V);
assert(found != Temporaries.end());
mv = SGF.emitManagedRValueWithCleanup(found->second);
} else {
SILValue arg = caseBB->getArgument(argIndex++);
assert(arg.getOwnershipKind() == ValueOwnershipKind::Owned ||
arg.getOwnershipKind() == ValueOwnershipKind::Any);
mv = SGF.emitManagedRValueWithCleanup(arg);
}

if (V->isLet()) {
// Just emit a let and leave the cleanup alone.
SGF.VarLocs[V].value = mv.getValue();
return;
}

// Otherwise, the pattern variables were all emitted as lets and one got
// passed in. Since we have a var, alloc a box for the var and forward in
// the chosen value.
SGF.VarLocs.erase(V);
auto newVar = SGF.emitInitializationForVarDecl(V, V->isLet());
newVar->copyOrInitValueInto(SGF, V, mv, /*isInit*/ true);
newVar->finishInitialization(SGF);
});

// Now that we have setup all of the VarLocs correctly, emit the shared case
// body.
emitCaseBody(caseBlock);
}
}

Expand Down Expand Up @@ -2629,6 +2640,81 @@ static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
SGFContext());
}

static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
PatternMatchEmission &emission,
ArgArray argArray, ClauseRow &row) {
auto caseBlock = row.getClientData<CaseStmt>();
SGF.emitProfilerIncrement(caseBlock);

// Certain case statements can be entered along multiple paths, either
// because they have multiple labels or because of fallthrough. When we
// need multiple entrance path, we factor the paths with a shared block.
if (!caseBlock->hasBoundDecls()) {
// Don't emit anything yet, we emit it at the cleanup level of the switch
// statement.
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
SGF.Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
return;
}

// If we don't have a fallthrough or a multi-pattern 'case', we can just
// emit the body inline and save some dead blocks. Emit the statement here.
if (!row.hasFallthroughTo() && caseBlock->getCaseLabelItems().size() == 1) {
emission.emitCaseBody(caseBlock);
return;
}

JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);

// Generate the arguments from this row's pattern in the case block's
// expected order, and keep those arguments from being cleaned up, as
// we're passing the +1 along to the shared case block dest. (The
// cleanups still happen, as they are threaded through here messily,
// but the explicit retains here counteract them, and then the
// retain/release pair gets optimized out.)
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
SmallVector<SILValue, 4> args;
SmallVector<VarDecl *, 4> expectedVarOrder;
SmallVector<VarDecl *, 4> vars;
labelItems[0].getPattern()->collectVariables(expectedVarOrder);
row.getCasePattern()->collectVariables(vars);

SILModule &M = SGF.F.getModule();
for (auto expected : expectedVarOrder) {
if (!expected->hasName())
continue;
for (auto *var : vars) {
if (!var->hasName() || var->getName() != expected->getName())
continue;

SILValue value = SGF.VarLocs[var].value;
SILType type = value->getType();

// If we have an address-only type, initialize the temporary
// allocation. We're not going to pass the address as a block
// argument.
if (type.isAddressOnly(M)) {
emission.emitAddressOnlyInitialization(expected, value);
break;
}

// If we have a loadable address, perform a load [copy].
if (type.isAddress()) {
value = SGF.B.emitLoadValueOperation(SGF.CurrentSILLoc, value,
LoadOwnershipQualifier::Copy);
args.push_back(value);
break;
}

value = SGF.B.emitCopyValueOperation(SGF.CurrentSILLoc, value);
args.push_back(value);
break;
}
}

SGF.Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
}

void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
LLVM_DEBUG(llvm::dbgs() << "emitting switch stmt\n";
S->dump(llvm::dbgs());
Expand All @@ -2644,81 +2730,10 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
return;
}

auto completionHandler = [&](PatternMatchEmission &emission,
ArgArray argArray,
ClauseRow &row) {
auto caseBlock = row.getClientData<CaseStmt>();
emitProfilerIncrement(caseBlock);

// Certain case statements can be entered along multiple paths, either
// because they have multiple labels or because of fallthrough. When we
// need multiple entrance path, we factor the paths with a shared block.
if (!caseBlock->hasBoundDecls()) {
// Don't emit anything yet, we emit it at the cleanup level of the switch
// statement.
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
return;
}

// If we don't have a fallthrough or a multi-pattern 'case', we can just
// emit the body inline and save some dead blocks. Emit the statement here.
if (!row.hasFallthroughTo() && caseBlock->getCaseLabelItems().size() == 1) {
emission.emitCaseBody(caseBlock);
return;
}

JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);

// Generate the arguments from this row's pattern in the case block's
// expected order, and keep those arguments from being cleaned up, as
// we're passing the +1 along to the shared case block dest. (The
// cleanups still happen, as they are threaded through here messily,
// but the explicit retains here counteract them, and then the
// retain/release pair gets optimized out.)
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
SmallVector<SILValue, 4> args;
SmallVector<VarDecl *, 4> expectedVarOrder;
SmallVector<VarDecl *, 4> vars;
labelItems[0].getPattern()->collectVariables(expectedVarOrder);
row.getCasePattern()->collectVariables(vars);

SILModule &M = F.getModule();
for (auto expected : expectedVarOrder) {
if (!expected->hasName())
continue;
for (auto *var : vars) {
if (!var->hasName() || var->getName() != expected->getName())
continue;

SILValue value = VarLocs[var].value;
SILType type = value->getType();

// If we have an address-only type, initialize the temporary
// allocation. We're not going to pass the address as a block
// argument.
if (type.isAddressOnly(M)) {
emission.emitAddressOnlyInitialization(expected, value);
break;
}

// If we have a loadable address, perform a load [copy].
if (type.isAddress()) {
value = B.emitLoadValueOperation(CurrentSILLoc, value,
LoadOwnershipQualifier::Copy);
args.push_back(value);
break;
}

value = B.emitCopyValueOperation(CurrentSILLoc, value);
args.push_back(value);
break;
}
}

Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
auto completionHandler = [this](PatternMatchEmission &emission,
ArgArray argArray, ClauseRow &row) {
return switchCaseStmtSuccessCallback(*this, emission, argArray, row);
};

PatternMatchEmission emission(*this, S, completionHandler);

// Add a row for each label of each case.
Expand Down