Skip to content

[SILGen] Simpler impl of multiple matching patterns in case w/ variable bindings #5094

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
merged 1 commit into from
Oct 3, 2016
Merged
Show file tree
Hide file tree
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
94 changes: 25 additions & 69 deletions lib/SILGen/SILGenPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,15 +1017,15 @@ void PatternMatchEmission::emitWildcardDispatch(ClauseMatrix &clauses,
// Bind the rest of the patterns.
bindIrrefutablePatterns(clauses[row], args, !hasGuard, hasMultipleItems);

// Emit the guard branch, if it exists.
if (guardExpr) {
SGF.usingImplicitVariablesForPattern(clauses[row].getCasePattern(), dyn_cast<CaseStmt>(stmt), [&]{
SGF.usingImplicitVariablesForPattern(clauses[row].getCasePattern(), dyn_cast<CaseStmt>(stmt), [&]{
// Emit the guard branch, if it exists.
if (guardExpr) {
this->emitGuardBranch(guardExpr, guardExpr, failure);
});
}
}

// Enter the row.
CompletionHandler(*this, args, clauses[row]);
// Enter the row.
CompletionHandler(*this, args, clauses[row]);
});
assert(!SGF.B.hasValidInsertionPoint());
}

Expand Down Expand Up @@ -1168,13 +1168,6 @@ void PatternMatchEmission::bindVariable(SILLocation loc, VarDecl *var,
CanType formalValueType,
bool isIrrefutable,
bool hasMultipleItems) {
// If this binding is one of multiple patterns, each individual binding
// will just be let, and then the chosen value will get forwarded into
// a var box in the final shared case block.
bool forcedLet = hasMultipleItems && !var->isLet();
if (forcedLet)
var->setLet(true);

// Initialize the variable value.
InitializationPtr init = SGF.emitInitializationForVarDecl(var);

Expand All @@ -1184,9 +1177,6 @@ void PatternMatchEmission::bindVariable(SILLocation loc, VarDecl *var,
} else {
std::move(rv).copyInto(SGF, loc, init.get());
}

if (forcedLet)
var->setLet(false);
}

/// Evaluate a guard expression and, if it returns false, branch to
Expand Down Expand Up @@ -2099,35 +2089,36 @@ void SILGenFunction::usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt

ArrayRef<CaseLabelItem> labelItems = stmt->getCaseLabelItems();
auto expectedPattern = labelItems[0].getPattern();

if (labelItems.size() <= 1 || pattern == expectedPattern) {
f();
return;
}

// Remap vardecls that the case body is expecting to the pattern var locations
// for the given pattern, emit whatever, and switch back.
SmallVector<VarDecl *, 4> Vars;
expectedPattern->collectVariables(Vars);
SmallVector<VarDecl *, 4> expectedVars;
SmallVector<VarLoc, 4> savedVarLocs;
expectedPattern->collectVariables(expectedVars);

for (auto expected : expectedVars)
savedVarLocs.push_back(VarLocs[expected]);

auto variableSwapper = [&] {
pattern->forEachVariable([&](VarDecl *VD) {
if (!VD->hasName())
pattern->forEachVariable([&](VarDecl *VD) {
if (!VD->hasName())
return;
for (auto expected : expectedVars) {
if (expected->hasName() && expected->getName() == VD->getName()) {
VarLocs[expected] = VarLocs[VD];
return;
for (auto expected : Vars) {
if (expected->hasName() && expected->getName() == VD->getName()) {
auto swap = VarLocs[expected];
VarLocs[expected] = VarLocs[VD];
VarLocs[VD] = swap;
return;
}
}
});
};
}
});

variableSwapper();
f();
variableSwapper();

for (unsigned index = 0; index < savedVarLocs.size(); index++)
VarLocs[expectedVars[index]] = savedVarLocs[index];
}

void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
Expand All @@ -2154,41 +2145,6 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock,
row.hasFallthroughTo());
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
} else if (caseBlock->getCaseLabelItems().size() > 1) {
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock,
row.hasFallthroughTo());

// 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);

for (auto expected : expectedVarOrder) {
if (!expected->hasName())
continue;
for (auto var : Vars) {
if (var->hasName() && var->getName() == expected->getName()) {
auto value = VarLocs[var].value;
args.push_back(value);

for (auto cmv : argArray) {
if (cmv.getValue() == value) {
B.createRetainValue(CurrentSILLoc, value, Atomicity::Atomic);
break;
}
}
break;
}
}
}
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
} else {
// However, if we don't have a fallthrough or a multi-pattern 'case', we
// can just emit the body inline and save some dead blocks.
Expand Down
68 changes: 37 additions & 31 deletions test/SILGen/switch_var.swift
Original file line number Diff line number Diff line change
Expand Up @@ -471,15 +471,14 @@ func test_multiple_patterns1() {
// CHECK: cond_br {{%.*}}, [[FIRST_MATCH_CASE:bb[0-9]+]], [[FIRST_FAIL:bb[0-9]+]]
// CHECK: [[FIRST_MATCH_CASE]]:
// CHECK: debug_value [[FIRST_X:%.*]] :
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[FIRST_X]] : $Int)
// CHECK: [[B1_FUNC:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[B1_FUNC]]([[FIRST_X]])
// CHECK: [[FIRST_FAIL]]:
// CHECK: cond_br {{%.*}}, [[SECOND_MATCH_CASE:bb[0-9]+]], [[SECOND_FAIL:bb[0-9]+]]
// CHECK: [[SECOND_MATCH_CASE]]:
// CHECK: debug_value [[SECOND_X:%.*]] :
// CHECK: br [[CASE_BODY]]([[SECOND_X]] : $Int)
// CHECK: [[CASE_BODY]]([[BODY_VAR:%.*]] : $Int):
// CHECK: [[A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[A]]([[BODY_VAR]])
// CHECK: [[B2_FUNC:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[B2_FUNC]]([[SECOND_X]])
a(x: x)
default:
// CHECK: [[SECOND_FAIL]]:
Expand All @@ -502,16 +501,15 @@ func test_multiple_patterns2() {
// CHECK: apply {{%.*}}([[FIRST_X]], [[T1]])
// CHECK: cond_br {{%.*}}, [[FIRST_MATCH_CASE:bb[0-9]+]], [[FIRST_FAIL:bb[0-9]+]]
// CHECK: [[FIRST_MATCH_CASE]]:
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[FIRST_X]] : $Int)
// CHECK: [[A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[A]]([[FIRST_X]])
// CHECK: [[FIRST_FAIL]]:
// CHECK: debug_value [[SECOND_X:%.*]] :
// CHECK: apply {{%.*}}([[SECOND_X]], [[T2]])
// CHECK: cond_br {{%.*}}, [[SECOND_MATCH_CASE:bb[0-9]+]], [[SECOND_FAIL:bb[0-9]+]]
// CHECK: [[SECOND_MATCH_CASE]]:
// CHECK: br [[CASE_BODY]]([[SECOND_X]] : $Int)
// CHECK: [[CASE_BODY]]([[BODY_VAR:%.*]] : $Int):
// CHECK: [[A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[A]]([[BODY_VAR]])
// CHECK: apply [[A]]([[SECOND_X]])
a(x: x)
default:
// CHECK: [[SECOND_FAIL]]:
Expand All @@ -535,22 +533,22 @@ func test_multiple_patterns3() {
// CHECK: [[A]]({{%.*}} : $(Int, Double)):
// CHECK: [[A_X:%.*]] = tuple_extract
// CHECK: [[A_N:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[A_X]] : $Int, [[A_N]] : $Double)
// CHECK: [[FUNC_A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_A]]([[A_X]])

// CHECK: [[B]]({{%.*}} : $(Double, Int)):
// CHECK: [[B_N:%.*]] = tuple_extract
// CHECK: [[B_X:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[B_X]] : $Int, [[B_N]] : $Double)
// CHECK: [[FUNC_B:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_B]]([[B_X]])

// CHECK: [[C]]({{%.*}} : $(Int, Int, Double)):
// CHECK: [[C__:%.*]] = tuple_extract
// CHECK: [[C_X:%.*]] = tuple_extract
// CHECK: [[C_N:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[C_X]] : $Int, [[C_N]] : $Double)
// CHECK: [[FUNC_C:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_C]]([[C_X]])

// CHECK: [[CASE_BODY]]([[BODY_X:%.*]] : $Int, [[BODY_N:%.*]] : $Double):
// CHECK: [[FUNC_A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_A]]([[BODY_X]])
a(x: x)
}
}
Expand All @@ -574,24 +572,25 @@ func test_multiple_patterns4() {
// CHECK: [[A]]({{%.*}} : $(Int, Double)):
// CHECK: [[A_X:%.*]] = tuple_extract
// CHECK: [[A_N:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[A_X]] : $Int)
// CHECK: [[FUNC_A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_A]]([[A_X]])

// CHECK: [[B]]({{%.*}} : $(Double, Int)):
// CHECK: [[B_N:%.*]] = tuple_extract
// CHECK: [[B_X:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[B_X]] : $Int)
// CHECK: [[FUNC_B:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_B]]([[B_X]])

// CHECK: [[C]]({{%.*}} : $(Int, Int, Double)):
// CHECK: br [[CASE_BODY]]([[Y_X]] : $Int)
// CHECK: [[FUNC_C:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_C]]([[Y_X]])

// CHECK: [[Z]]({{%.*}} : $(Int, Foo)):
// CHECK: [[Z_X:%.*]] = tuple_extract
// CHECK: [[Z_F:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[Z_X]] : $Int)
// CHECK: [[FUNC_Z:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_Z]]([[Z_X]])

// CHECK: [[CASE_BODY]]([[BODY_X:%.*]] : $Int):
// CHECK: [[FUNC_A:%.*]] = function_ref @_TF10switch_var1aFT1xSi_T_
// CHECK: apply [[FUNC_A]]([[BODY_X]])
a(x: x)
}
}
Expand All @@ -612,25 +611,32 @@ func test_multiple_patterns5() {
// CHECK: [[A]]({{%.*}} : $(Int, Double)):
// CHECK: [[A_X:%.*]] = tuple_extract
// CHECK: [[A_N:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[A_X]] : $Int)
// CHECK: [[A_BOX:%.*]] = project_box
// CHECK: store [[A_X]] to [[A_BOX]]
// CHECK: [[FUNC_A:%.*]] = function_ref @_TF10switch_var3aaaFT1xRSi_T_
// CHECK: apply [[FUNC_A]]([[A_BOX]])

// CHECK: [[B]]({{%.*}} : $(Double, Int)):
// CHECK: [[B_N:%.*]] = tuple_extract
// CHECK: [[B_X:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[B_X]] : $Int)
// CHECK: [[B_BOX:%.*]] = project_box
// CHECK: store [[B_X]] to [[B_BOX]]
// CHECK: [[FUNC_B:%.*]] = function_ref @_TF10switch_var3aaaFT1xRSi_T_
// CHECK: apply [[FUNC_B]]([[B_BOX]])

// CHECK: [[C]]({{%.*}} : $(Int, Int, Double)):
// CHECK: br [[CASE_BODY]]([[Y_X]] : $Int)
// CHECK: [[Y_BOX:%.*]] = project_box
// CHECK: store [[Y_X]] to [[Y_BOX]]
// CHECK: [[FUNC_C:%.*]] = function_ref @_TF10switch_var3aaaFT1xRSi_T_
// CHECK: apply [[FUNC_C]]([[Y_BOX]])

// CHECK: [[Z]]({{%.*}} : $(Int, Foo)):
// CHECK: [[Z_X:%.*]] = tuple_extract
// CHECK: [[Z_F:%.*]] = tuple_extract
// CHECK: br [[CASE_BODY]]([[Z_X]] : $Int)

// CHECK: [[CASE_BODY]]([[BODY_X:%.*]] : $Int):
// CHECK: store [[BODY_X]] to [[BOX_X:%.*]] : $*Int
// CHECK: [[FUNC_AAA:%.*]] = function_ref @_TF10switch_var3aaaFT1xRSi_T_
// CHECK: apply [[FUNC_AAA]]([[BOX_X]])
// CHECK: [[Z_BOX:%.*]] = project_box
// CHECK: store [[Z_X]] to [[Z_BOX]]
// CHECK: [[FUNC_Z:%.*]] = function_ref @_TF10switch_var3aaaFT1xRSi_T_
// CHECK: apply [[FUNC_Z]]([[Z_BOX]])
aaa(x: &x)
}
}
Expand Down