Search code examples
loopsassemblyloop-unrolling

Generic pattern for a loop- pintool


I'm trying to find a generic pattern to be able to run a pintool program so it will always give me where is the index or what is it, and to which value the loop goes to.

For example, here is the assembly of a certain loop:

 40c374:    48 8b 55 e8             mov    -0x18(%rbp),%rdx
  40c378:   8b 45 fc                mov    -0x4(%rbp),%eax
  40c37b:   48 98                   cltq   
  40c37d:   0f b6 84 02 80 00 00    movzbl 0x80(%rdx,%rax,1),%eax
  40c384:   00 
  40c385:   84 c0                   test   %al,%al
  40c387:   74 2a                   je     40c3b3 <makeMaps_e+0x5b>
  40c389:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c38d:   8b 40 7c                mov    0x7c(%rax),%eax
  40c390:   89 c1                   mov    %eax,%ecx
  40c392:   48 8b 55 e8             mov    -0x18(%rbp),%rdx
  40c396:   8b 45 fc                mov    -0x4(%rbp),%eax
  40c399:   48 98                   cltq   
  40c39b:   88 8c 02 80 01 00 00    mov    %cl,0x180(%rdx,%rax,1)
  40c3a2:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c3a6:   8b 40 7c                mov    0x7c(%rax),%eax
  40c3a9:   8d 50 01                lea    0x1(%rax),%edx
  40c3ac:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c3b0:   89 50 7c                mov    %edx,0x7c(%rax)
  40c3b3:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
  40c3b7:   81 7d fc ff 00 00 00    cmpl   $0xff,-0x4(%rbp)
  40c3be:   7e b4                   jle    40c374 <makeMaps_e+0x1c>

Now I have noticed that the Check CMD is not always CMP...

Is there a way of finding out the index value and total number of iterations?


Solution

  • As @Ped7g says, not all loops have a trip-count that's known before the first iteration runs.

    But for loops that are, they're normally compiled with only one conditional branch inside the loop (normally at the bottom). A heuristic like this may detect them.

    • Find the loop-branch.

      If there are multiple conditional branches inside the loop, and one of them leaves the loop and doesn't come back, or skips the counter increment, then this isn't a simple loop.

      Nested loops or conditionals inside the loop body aren't a problem, if you can verify that they don't modify the loop-counter for the (outer) loop you're looking at.

    • find the register(s) it depends on. e.g. cmp p, end_pointer / jne or dec eax / jnz

      e.g. work backwards from the loop branch to the first flag-setting instruction. (If the compiler is optimizing for macro-fusion, then they will be back-to-back, but unfortunately that's not always enabled in generic tunings.) Compilers normally avoid partial-flag shenanigans (like sub ecx, 1 / dec eax / jnc), but if you want to be extra safe make sure the flag-setting instruction actually sets the flags that the branch reads.

    • check that at only one of those registers is actually modified in the loop.

    • check that the modified register (loop counter) is only modified by a constant amount every iteration

    Given the start value of the loop counter, and the end value it's compared against, you can calculate the trip-count. If any of those checks fail, it's not a "simple loop" according to this heuristic.

    If you want it to work on un-optimized code, you'll have to follow the loop counter through store/reload to memory. Even optimizing compilers sometimes emit multiple instructions that touch the loop counter, like lea ecx, [rdi+1] then use ecx for a while, then mov edi, ecx. Usually this looks like a missed-optimization (more instructions in the loop to have stuff where the compiler wants outside the loop) but it happens.