-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Poor decompilation of nested logical expressions #1133
Comments
The current support for short-circuit operators is based on few hardcoded code patterns in |
Further analysis reveals that these expressions always result in a pattern of two successive if statements with the same branch target, which could be merged with Example:
I'll test a fix and submit a PR. |
merging successive inlined ifs works well, but matching and transformation flow of Subquestion: Is there any need to keep single branch blocks when transforming, or can the result of |
In addition to the tests, what we're usually doing with significant decompiler changes:
For branch instructions there shouldn't be a difference. In general, |
I had a lot of success adding better pattern matching cases and inversions to the ConditionDetection, however on review it was hard to determine the necessity of when to recheck for or perform certain simplifications or inversions so I'm now looking at a more fundamental approach based on just inlines, inversions and removing redundant gotos. Currently all the commented test cases in Loops.cs succeed, as well as some more complex tests of my own but it's not quite complete. Are there any other known, previously marked as "TODO" control flow tests? |
I'm not aware of any other than those you found in |
I'm in the process of organising tests and I'm wondering if there's a way to add "counter cases" things which produce a known different output when decompiled. For example, one of the restored test cases public int InterestingLoop()
{
int num = 0;
if (num % 11 == 0) {
while (true) {
if (num % 4 == 0) {
if (num % 7 == 0) {
if (num % 11 == 0) {
// use a continue here to prevent moving the if (i%7) outside the loop
continue;
}
Console.WriteLine("7");
} else {
// this block is not part of the natural loop
Console.WriteLine("!7");
}
break;
}
num++;
}
// This instruction is still dominated by the loop header
num = -2147483648;//int.MinValue;
}
return num;
} actually decompiles to public int InterestingLoop()
{
int num = 0;
if (num % 11 == 0)
{
while (true)
{
if (num % 4 == 0)
{
if (num % 7 != 0)
{
Console.WriteLine("!7");
break;
}
if (num % 11 != 0)
{
Console.WriteLine("7");
break;
}
}
else
num++;
}
num = -2147483648;
}
return num;
} Which is actually quite good to see it successfully removes use of "continue" as a priority. Another example would be the switch switch (I) {
case 1:
case 4:
case 7:
Console.WriteLine();
break;
} decompiles to
|
Additionally, the public void WhileWithGoto()
{
while (Condition("Main Loop")) {
if (Condition("Condition")) {
goto IL_000f;
}
goto IL_0026;
IL_000f:
Console.WriteLine("Block1");
if (Condition("Condition2")) {
continue;
}
goto IL_0026;
IL_0026:
Console.WriteLine("Block2");
goto IL_000f;
}
} test compiles fine, but there's no label matching, it'd be nice if there was a way to specify a given test will only pass exactly with certain compile/debug configurations |
Finally, is there a way to specify that a test won't pass on certain compiler combinations? For example, often generated temporaries in non-optimised code make it impossible to perform certain transformations. Edit: I was able to change the transformation pipeline slightly so that these tests pass again |
You can use |
As the number of nested || and && operators within a statement increases, the decompiler makes increasingly questionable decisions on how to represent the final control flow.
There are many semantically equivalent representations, and some of them can only be decided via heuristics. For a simple example
if(!a) return;
vs nesting the entire method inif (a) {}
. However the comparison tests below leave plenty of room for improvement.Basic if statement side by side http://www.mergely.com/RkWr5GxE/
It is reasonable to expect the decompiler to pick a simplified control flow representation, and to favour nested if statements over arbitrarily long and complex expressions, however the introduction of gotos in tests 4a and 5 shows that even some simple patterns which you would expect to a code review are badly handled.
If the expression target is not a branch condition, then it handles better, but there's still room for improvement http://www.mergely.com/xWlXZH7o/
Referencing #1094
ILAst analysis to come
The text was updated successfully, but these errors were encountered: