Skip to content

Commit 94ca715

Browse files
authored
Merge pull request #13988 from slavapestov/address-only-switch
SILGen: Support multiple-entry case blocks with address-only bindings
2 parents 3f5a710 + 162187c commit 94ca715

File tree

6 files changed

+440
-157
lines changed

6 files changed

+440
-157
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,6 @@ ERROR(writeback_overlap_subscript,none,
8484
NOTE(writebackoverlap_note,none,
8585
"concurrent writeback occurred here", ())
8686

87-
ERROR(addressonly_type_used_in_multipattern_case,none,
88-
"matching %select{type '%1'|a protocol value|a generic value}0 in multiple patterns "
89-
"is not yet supported; use separate cases instead",
90-
(unsigned, Type))
91-
9287
ERROR(inout_argument_alias,none,
9388
"inout arguments are not allowed to alias each other", ())
9489
NOTE(previous_inout_alias,none,

lib/SILGen/SILGenPattern.cpp

Lines changed: 135 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ class PatternMatchEmission {
408408
CleanupsDepth PatternMatchStmtDepth;
409409
llvm::MapVector<CaseStmt*, std::pair<SILBasicBlock*, bool>> SharedCases;
410410

411+
llvm::DenseMap<VarDecl*, SILValue> Temporaries;
412+
411413
using CompletionHandlerTy =
412414
llvm::function_ref<void(PatternMatchEmission &, ArgArray, ClauseRow &)>;
413415
CompletionHandlerTy CompletionHandler;
@@ -416,13 +418,19 @@ class PatternMatchEmission {
416418
PatternMatchEmission(SILGenFunction &SGF, Stmt *S,
417419
CompletionHandlerTy completionHandler)
418420
: SGF(SGF), PatternMatchStmt(S),
419-
PatternMatchStmtDepth(SGF.getCleanupsDepth()),
420421
CompletionHandler(completionHandler) {}
421422

422423
void emitDispatch(ClauseMatrix &matrix, ArgArray args,
423424
const FailureHandler &failure);
424425

425-
JumpDest getSharedCaseBlockDest(CaseStmt *caseStmt, bool hasFallthroughTo);
426+
void initSharedCaseBlockDest(CaseStmt *caseBlock, bool hasFallthroughTo);
427+
428+
void emitAddressOnlyAllocations();
429+
430+
void emitAddressOnlyInitialization(VarDecl *dest, SILValue value);
431+
432+
JumpDest getSharedCaseBlockDest(CaseStmt *caseStmt);
433+
426434
void emitSharedCaseBlocks();
427435

428436
void emitCaseBody(CaseStmt *caseBlock);
@@ -1759,9 +1767,8 @@ void PatternMatchEmission::emitEnumElementDispatchWithOwnership(
17591767
enumDecl = SGF.getASTContext().getOptionalDecl();
17601768
}
17611769

1762-
// FIXME: Get expansion from SILFunction
17631770
if (!enumDecl->isResilient(SGF.SGM.M.getSwiftModule(),
1764-
ResilienceExpansion::Maximal)) {
1771+
SGF.F.getResilienceExpansion())) {
17651772
exhaustive = true;
17661773

17671774
for (auto elt : enumDecl->getAllElements()) {
@@ -1988,7 +1995,6 @@ void PatternMatchEmission::emitEnumElementDispatch(
19881995
enumDecl = SGF.getASTContext().getOptionalDecl();
19891996
}
19901997

1991-
// FIXME: Get expansion from SILFunction
19921998
if (!enumDecl->isResilient(SGF.SGM.M.getSwiftModule(),
19931999
SGF.F.getResilienceExpansion())) {
19942000
exhaustive = true;
@@ -2326,43 +2332,77 @@ void PatternMatchEmission::emitCaseBody(CaseStmt *caseBlock) {
23262332
}
23272333
}
23282334

2329-
/// Retrieve the jump destination for a shared case block.
2330-
JumpDest PatternMatchEmission::getSharedCaseBlockDest(CaseStmt *caseBlock,
2331-
bool hasFallthroughTo) {
2335+
void PatternMatchEmission::initSharedCaseBlockDest(CaseStmt *caseBlock,
2336+
bool hasFallthroughTo) {
23322337
auto result = SharedCases.insert({caseBlock, {nullptr, hasFallthroughTo}});
2338+
assert(result.second);
23332339

2334-
// If there's already an entry, use that.
2335-
SILBasicBlock *block;
2336-
if (!result.second) {
2337-
block = result.first->second.first;
2338-
assert(block);
2339-
} else {
2340-
// Create the shared destination at the first place that might
2341-
// have needed it.
2342-
block = SGF.createBasicBlock();
2343-
result.first->second.first = block;
2340+
auto *block = SGF.createBasicBlock();
2341+
result.first->second.first = block;
23442342

2345-
// Add args for any pattern variables
2343+
// Add args for any pattern variables
2344+
if (caseBlock->hasBoundDecls()) {
2345+
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
2346+
pattern->forEachVariable([&](VarDecl *V) {
2347+
if (!V->hasName())
2348+
return;
2349+
2350+
// We don't pass address-only values in basic block arguments.
2351+
SILType ty = SGF.getLoweredType(V->getType());
2352+
if (ty.isAddressOnly(SGF.F.getModule()))
2353+
return;
2354+
block->createPHIArgument(ty, ValueOwnershipKind::Owned, V);
2355+
});
2356+
}
2357+
}
2358+
2359+
/// Retrieve the jump destination for a shared case block.
2360+
JumpDest PatternMatchEmission::getSharedCaseBlockDest(CaseStmt *caseBlock) {
2361+
auto result = SharedCases.find(caseBlock);
2362+
assert(result != SharedCases.end());
2363+
2364+
auto *block = result->second.first;
2365+
assert(block);
2366+
2367+
return JumpDest(block, PatternMatchStmtDepth,
2368+
CleanupLocation(PatternMatchStmt));
2369+
}
2370+
2371+
void PatternMatchEmission::emitAddressOnlyAllocations() {
2372+
for (auto &entry: SharedCases) {
2373+
CaseStmt *caseBlock = entry.first;
2374+
2375+
// If we have a shared case with bound decls, then the 0th pattern has the
2376+
// order of variables that are the incoming BB arguments. Setup the VarLocs
2377+
// to point to the incoming args and setup initialization so any args needing
2378+
// cleanup will get that as well.
23462379
if (caseBlock->hasBoundDecls()) {
23472380
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
23482381
pattern->forEachVariable([&](VarDecl *V) {
23492382
if (!V->hasName())
23502383
return;
2351-
// We should never PHI addresses. To eliminate that possibility, we:
2352-
//
2353-
// 1. Load all loadable types and pass them as objects to the block.
2354-
// 2. We do not emit arguments for address only types. We instead just
2355-
// assign SILUndef to the VarLoc.
2384+
23562385
SILType ty = SGF.getLoweredType(V->getType());
2357-
if (ty.isAddressOnly(SGF.F.getModule()))
2386+
2387+
if (ty.isAddressOnly(SGF.F.getModule())) {
2388+
assert(!Temporaries[V]);
2389+
Temporaries[V] = SGF.emitTemporaryAllocation(V, ty);
23582390
return;
2359-
block->createPHIArgument(ty, ValueOwnershipKind::Owned, V);
2391+
}
23602392
});
23612393
}
23622394
}
23632395

2364-
return JumpDest(block, PatternMatchStmtDepth,
2365-
CleanupLocation(PatternMatchStmt));
2396+
// Now we have all of our cleanups entered, so we can record the
2397+
// depth.
2398+
PatternMatchStmtDepth = SGF.getCleanupsDepth();
2399+
}
2400+
2401+
void PatternMatchEmission::
2402+
emitAddressOnlyInitialization(VarDecl *dest, SILValue value) {
2403+
auto found = Temporaries.find(dest);
2404+
assert(found != Temporaries.end());
2405+
SGF.B.createCopyAddr(dest, value, found->second, IsNotTake, IsInitialization);
23662406
}
23672407

23682408
/// Emit all the shared case statements.
@@ -2388,8 +2428,13 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
23882428
SGF.B.setInsertionPoint(predBB);
23892429

23902430
} else {
2431+
// FIXME: Figure out why this is necessary.
2432+
if (caseBB->pred_empty()) {
2433+
SGF.eraseBasicBlock(caseBB);
2434+
continue;
2435+
}
2436+
23912437
// Otherwise, move the block to after the first predecessor.
2392-
assert(!caseBB->pred_empty() && "Emitted an unused shared block?");
23932438
auto predBB = *caseBB->pred_begin();
23942439
caseBB->moveAfter(predBB);
23952440

@@ -2412,27 +2457,37 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
24122457
return;
24132458

24142459
SILType ty = SGF.getLoweredType(V->getType());
2460+
2461+
SILValue value;
24152462
if (ty.isAddressOnly(SGF.F.getModule())) {
2416-
// Just assign SILUndef as a value for address only values.
2417-
SGF.VarLocs[V].value = SILUndef::get(ty, SGF.F.getModule());
2418-
return;
2463+
// There's no basic block argument, since we don't allow basic blocks
2464+
// to have address arguments.
2465+
//
2466+
// Instead, we map the variable to a temporary alloc_stack in
2467+
// emitAddressOnlyAllocations(), and store into it at each
2468+
// predecessor block.
2469+
//
2470+
// There's nothing to do here, since the value should already have
2471+
// been initialized on entry.
2472+
auto found = Temporaries.find(V);
2473+
assert(found != Temporaries.end());
2474+
value = found->second;
2475+
} else {
2476+
value = caseBB->getArgument(argIndex++);
24192477
}
24202478

24212479
if (V->isLet()) {
24222480
// Just emit a let with cleanup.
2423-
SGF.VarLocs[V].value = caseBB->getArgument(argIndex++);
2424-
SGF.emitInitializationForVarDecl(V, V->isLet())
2425-
->finishInitialization(SGF);
2481+
SGF.VarLocs[V].value = value;
2482+
SGF.enterDestroyCleanup(value);
24262483
} else {
24272484
// The pattern variables were all emitted as lets and one got passed in,
24282485
// now we finally alloc a box for the var and forward in the chosen value.
24292486
SGF.VarLocs.erase(V);
24302487
auto newVar = SGF.emitInitializationForVarDecl(V, V->isLet());
2431-
auto loc = SGF.CurrentSILLoc;
2432-
auto value =
2433-
ManagedValue::forUnmanaged(caseBB->getArgument(argIndex++));
2434-
auto formalType = V->getType()->getCanonicalType();
2435-
RValue(SGF, loc, formalType, value).forwardInto(SGF, loc, newVar.get());
2488+
auto mv = ManagedValue::forUnmanaged(value);
2489+
newVar->copyOrInitValueInto(SGF, V, mv, /*isInit*/ true);
2490+
newVar->finishInitialization(SGF);
24362491
}
24372492
});
24382493
emitCaseBody(caseBlock);
@@ -2531,35 +2586,6 @@ void SILGenFunction::usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt
25312586
variableSwapper();
25322587
}
25332588

2534-
static void diagnoseMultiPatternCaseAddressOnlyBinding(SILGenFunction &SGF,
2535-
ValueDecl *decl,
2536-
SILValue value) {
2537-
SILLocation loc(decl);
2538-
2539-
// Try to figure out why this is an address only type. This is just an
2540-
// approximation. The targets of interest are:
2541-
//
2542-
// 1. existentials.
2543-
// 2. generics.
2544-
//
2545-
// If we are unable to show that we have an existential or generic, we use the
2546-
// more general unknown_addressonly_type_used_in_multipattern_case diagnostic.
2547-
unsigned errorPatternIndex = 0;
2548-
CanType ty = value->getType().getSwiftRValueType();
2549-
2550-
if (ty.findIf([&](Type ty) -> bool {
2551-
return ty->is<ProtocolType>() || ty->is<ProtocolCompositionType>();
2552-
})) {
2553-
errorPatternIndex = 1;
2554-
} else if (ty.findIf(
2555-
[&](Type ty) -> bool { return ty->is<ArchetypeType>(); })) {
2556-
errorPatternIndex = 2;
2557-
}
2558-
2559-
SGF.SGM.diagnose(loc, diag::addressonly_type_used_in_multipattern_case,
2560-
errorPatternIndex, ty);
2561-
}
2562-
25632589
void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25642590
DEBUG(llvm::dbgs() << "emitting switch stmt\n";
25652591
S->print(llvm::dbgs());
@@ -2576,12 +2602,6 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25762602
emitIgnoredExpr(S->getSubjectExpr());
25772603
return failure(SILLocation(S));
25782604
}
2579-
2580-
SILBasicBlock *contBB = createBasicBlock();
2581-
emitProfilerIncrement(S);
2582-
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));
2583-
2584-
bool diagnosedError = false;
25852605

25862606
auto completionHandler = [&](PatternMatchEmission &emission,
25872607
ArgArray argArray,
@@ -2595,23 +2615,18 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25952615
if (!caseBlock->hasBoundDecls()) {
25962616
// Don't emit anything yet, we emit it at the cleanup level of the switch
25972617
// statement.
2598-
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock,
2599-
row.hasFallthroughTo());
2618+
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock);
26002619
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
26012620
} else if (caseBlock->getCaseLabelItems().size() > 1) {
26022621
JumpDest sharedDest =
2603-
emission.getSharedCaseBlockDest(caseBlock, row.hasFallthroughTo());
2622+
emission.getSharedCaseBlockDest(caseBlock);
26042623

2605-
// Generate the arguments from this row's pattern in the case block's expected order,
2606-
// and keep those arguments from being cleaned up, as we're passing the +1 along to
2607-
// the shared case block dest. (The cleanups still happen, as they are threaded through
2608-
// here messily, but the explicit retains here counteract them, and then the
2624+
// Generate the arguments from this row's pattern in the case block's
2625+
// expected order, and keep those arguments from being cleaned up, as
2626+
// we're passing the +1 along to the shared case block dest. (The
2627+
// cleanups still happen, as they are threaded through here messily,
2628+
// but the explicit retains here counteract them, and then the
26092629
// retain/release pair gets optimized out.)
2610-
//
2611-
// *NOTE*. We assume that all values are passed as objects for
2612-
// simplicity. This is ok to do since any time we diagnose an error, we
2613-
// pass SILUndef to the shared case block. This is to maintain the CFG
2614-
// structure and thus prevent spurious 'dead code' warnings.
26152630
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
26162631
SmallVector<SILValue, 4> args;
26172632
SmallVector<VarDecl *, 4> expectedVarOrder;
@@ -2627,10 +2642,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26272642
if (var->hasName() && var->getName() == expected->getName()) {
26282643
SILValue value = VarLocs[var].value;
26292644
SILType type = value->getType();
2645+
2646+
// If we have an address-only type, initialize the temporary
2647+
// allocation. We're not going to pass the address as a block
2648+
// argument.
26302649
if (type.isAddressOnly(M)) {
2631-
if (!diagnosedError)
2632-
diagnoseMultiPatternCaseAddressOnlyBinding(*this, var, value);
2633-
diagnosedError = true;
2650+
emission.emitAddressOnlyInitialization(expected, value);
26342651
break;
26352652
}
26362653

@@ -2659,19 +2676,6 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26592676
};
26602677

26612678
PatternMatchEmission emission(*this, S, completionHandler);
2662-
2663-
Scope switchScope(Cleanups, CleanupLocation(S));
2664-
2665-
// Enter a break/continue scope. If we wanted a continue
2666-
// destination, it would probably be out here.
2667-
BreakContinueDestStack.push_back({S, contDest, JumpDest(S)});
2668-
2669-
PatternMatchContext switchContext = { emission };
2670-
SwitchStack.push_back(&switchContext);
2671-
2672-
// Emit the subject value. Dispatching will consume it.
2673-
ManagedValue subjectMV = emitRValueAsSingleValue(S->getSubjectExpr());
2674-
auto subject = ConsumableManagedValue::forOwned(subjectMV);
26752679

26762680
// Add a row for each label of each case.
26772681
//
@@ -2681,6 +2685,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26812685
clauseRows.reserve(S->getRawCases().size());
26822686
bool hasFallthrough = false;
26832687
for (auto caseBlock : S->getCases()) {
2688+
if (!caseBlock->hasBoundDecls() ||
2689+
caseBlock->getCaseLabelItems().size() > 1 ||
2690+
hasFallthrough) {
2691+
emission.initSharedCaseBlockDest(caseBlock, hasFallthrough);
2692+
}
2693+
26842694
for (auto &labelItem : caseBlock->getCaseLabelItems()) {
26852695
clauseRows.emplace_back(caseBlock,
26862696
const_cast<Pattern*>(labelItem.getPattern()),
@@ -2691,6 +2701,27 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26912701
hasFallthrough = containsFallthrough(caseBlock->getBody());
26922702
}
26932703

2704+
// Emit alloc_stacks for address-only variables appearing in
2705+
// multiple-entry case blocks.
2706+
emission.emitAddressOnlyAllocations();
2707+
2708+
SILBasicBlock *contBB = createBasicBlock();
2709+
emitProfilerIncrement(S);
2710+
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));
2711+
2712+
Scope switchScope(Cleanups, CleanupLocation(S));
2713+
2714+
// Enter a break/continue scope. If we wanted a continue
2715+
// destination, it would probably be out here.
2716+
BreakContinueDestStack.push_back({S, contDest, JumpDest(S)});
2717+
2718+
PatternMatchContext switchContext = { emission };
2719+
SwitchStack.push_back(&switchContext);
2720+
2721+
// Emit the subject value. Dispatching will consume it.
2722+
ManagedValue subjectMV = emitRValueAsSingleValue(S->getSubjectExpr());
2723+
auto subject = ConsumableManagedValue::forOwned(subjectMV);
2724+
26942725
// Set up an initial clause matrix.
26952726
ClauseMatrix clauses(clauseRows);
26962727

@@ -2723,7 +2754,7 @@ void SILGenFunction::emitSwitchFallthrough(FallthroughStmt *S) {
27232754
// Get the destination block.
27242755
CaseStmt *caseStmt = S->getFallthroughDest();
27252756
JumpDest sharedDest =
2726-
context->Emission.getSharedCaseBlockDest(caseStmt, true);
2757+
context->Emission.getSharedCaseBlockDest(caseStmt);
27272758
Cleanups.emitBranchAndCleanups(sharedDest, S);
27282759
}
27292760

0 commit comments

Comments
 (0)