Skip to content

Decompiler: Optimise do-while inside if with same condition to while-do #8165

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

Open
LukeSerne opened this issue May 18, 2025 · 1 comment
Open
Assignees
Labels
Feature: Decompiler Status: Triage Information is being gathered

Comments

@LukeSerne
Copy link
Contributor

Is your feature request related to a problem? Please describe.
Ghidra fails to recognise that a do-while loop depending on the same condition as the enclosing "proper" if (i.e. it has no else clause), is actually a while loop.

An example:

void iterate(list_s *param_1)

{
  if (param_1 != NULL)
  {
    do
    {
      param_1 = param_1->next;
    } while (param_1 != NULL);
  }
  return;
}

Describe the solution you'd like
Ghidra should recognise that the condition of the if and do-while are the same, and these should be combined into a single while loop.

So the output should look like:

void iterate(list_s *param_1)

{
  while (param_1 != NULL)
  {
    param_1 = param_1->next;
  }
  return;
}

Describe alternatives you've considered
Keeping the decompiler output the way it is now, while correct, is unnecessarily verbose, and it is not clear at first sight that this is performing a while loop, especially when the loop body is long enough for the do-while condition to not be on the same screen as the if statement.

Additional context

The debug xml can be found inside this zip: ifdowhile.zip

From loading the xml in decomp_dbg:

[decomp]> restore /tmp/ifdowhile.xml
/tmp/ifdowhile.xml successfully loaded: PowerPC 4xx 32-bit big endian embedded core
[decomp]> lo fu iterate                                                                                             
Function iterate: 0x00000000
[decomp]> decompile
Decompiling iterate                                       
Decompilation complete
[decomp]> print C

void iterate(list_s *param_1)

{
  if (param_1 != NULL)
  {
    do
    {
      param_1 = param_1->next;
    } while (param_1 != NULL);
  }
  return;
}
[decomp]> print raw
0
Basic Block 0 0x00000000-0x0000000c
0x00000008:14:  u0x00000f80:1(0x00000008:14) = r3(i) != #0x0
0x0000000c:26:  goto r0x0000004c:1(free) if (u0x00000f80:1(0x00000008:14) == 0)
Basic Block 1 0x00000024-0x0000003c
0x00000024:95:  r31(0x00000024:95) = r31(0x00000034:46) ? r3(i)
0x00000034:af:  u0x10000017(0x00000034:af) = r31(0x00000024:95) -> #0x0
0x00000034:46:  r31(0x00000034:46) = *(ram,u0x10000017(0x00000034:af))
0x00000038:4e:  u0x00014900:1(0x00000038:4e) = r31(0x00000034:46) != #0x0
0x0000003c:58:  goto r0x00000024:1(free) if (u0x00014900:1(0x00000038:4e) != 0)
Basic Block 2 0x0000004c-0x00000054
0x00000054:61:  return(#0x0)
[decomp]> print tree block
0
  List block 0
    If block 0
      Basic Block 0 0x00000000-0x0000000c
      Dowhile block 1
        Basic Block 1 0x00000024-0x0000003c
    Basic Block 2 0x0000004c-0x00000054
[decomp]> print varnode %r3(i)
Variable: param_1
Type: list_s *

0: list_s * = r3(i) mapped tlock nlock (consumed=0xffffffff) (internal=0x5ddeb3cb02b0) (create=0x113)
0: list_s * = r31(0x00000024:95) (consumed=0xffffffff) (internal=0x5ddeb3ce52e0) (create=0x1ad)
0: list_s * = r31(0x00000034:46) (consumed=0xffffffff) (internal=0x5ddeb3c9ada0) (create=0xc6)

Even though the conditions of the CBRANCH ops in blocks 0 and 1 are not identical in the (optimised) p-code, (Block 0 checks r3(i), while Block 1 checks r31(0x00000034:46)), both varnodes correspond to the same high variable (namely param_1), so they are still equivalent in some sense.

@LukeSerne
Copy link
Contributor Author

While looking through the open decompiler-related issues some more, I think this is strongly related to #7166. Although that issue is about for loop recovery, it seems plausible to me that if the improvement suggested by this issue is implemented, Ghidra will be able to optimise the code in #7166 to a for-loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature: Decompiler Status: Triage Information is being gathered
Projects
None yet
Development

No branches or pull requests

3 participants