-
Notifications
You must be signed in to change notification settings - Fork 291
Description
Comments on switch cases are resulting in excessive nodes in the CFG that prevent one of the relooper optimizations from applying. This results in overly complex control flow. This affects both the master branch and the relooper2 work on legare/relooper-experiment.
Input:
switch (x) {
case 0: // comment
case 1: // comment
case 2:
x++;
}Expected output:
match x {
0 | 1 | 2 => {
x += 1;
}
_ => {}
}Actual output (on master):
let mut current_block: u64;
match x {
1 => {
current_block = 773005375064117377;
}
0 | 2 => {
current_block = 773005375064117377;
}
_ => {
current_block = 14155750587950065367;
}
}
match current_block {
773005375064117377 => {
x += 1;
}
_ => {}
}Details
As part of the relooper algorithm, when a switch has multiple cases that go to the same place we merge those cases into a single | pattern. This is important for our ability to nest the code for the combined switch case into the corresponding match arm, since translating each case into its own match arm would mean we need to place the shared logic after the match in some way (either with current_block or with labeled blocks/breaks).
The issue here is likely happening when we build the CFG that we feed to relooper. The way the pattern merging logic works is that it merges switch cases that go to the same CFG node. Writing out the CFG as C pseudocode:
entry:
switch (x) {
case 0: goto A;
case 1: goto A;
case 2: goto A;
default: goto B;
}
A:
x++;
goto B;
B:
return;When we add comments to each of the cases we end up seeing a CFG more like this:
entry:
switch (x) {
case 0: goto A;
case 1: goto B;
case 2: goto C;
default: goto D;
}
A:
goto B;
B:
goto C;
C:
x++;
goto D;
D:
return;The two CFGs represent the same behavior, but the extra nodes in the latter case prevent the case merging optimization from working.