Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't the optimizer eliminate High in a loop?

People said that Delphi produces quite-nice optimized code on integer operations. I try the following example in Delphi 2007, and see its assembly code produced by the compiler.

program p1000;
{$APPTYPE CONSOLE}

procedure test;
var
  arr: array of integer;
  i: integer;

begin
  SetLength(arr, 100);
  for i := 0 to High(arr) do
  begin
    if (i = High(arr)) then
    begin
      arr[i] := -9;
    end;
  end;
end;

begin
  test;
  readln;
end.

When building configuration is set to DEBUG, I can set a breakpoint and use shortkey Ctrl+Alt+D to see its assembly code, like this:

Project3.dpr.11: for i := 0 to High(arr) do
004045A1 8B45FC           mov eax,[ebp-$04]
004045A4 E8F7FAFFFF       call @DynArrayHigh
004045A9 8BF0             mov esi,eax
004045AB 85F6             test esi,esi
004045AD 7C1D             jl $004045cc
004045AF 46               inc esi
004045B0 33DB             xor ebx,ebx
Project3.dpr.13: if (i = High(arr)) then
004045B2 8B45FC           mov eax,[ebp-$04]
004045B5 E8E6FAFFFF       **call @DynArrayHigh**
004045BA 3BD8             cmp ebx,eax
004045BC 750A             jnz $004045c8
Project3.dpr.15: arr[i] := -9;
004045BE 8B45FC           mov eax,[ebp-$04]
004045C1 C70498F7FFFFFF   mov [eax+ebx*4],$fffffff7
Project3.dpr.17: end;
004045C8 43               inc ebx
Project3.dpr.11: for i := 0 to High(arr) do
004045C9 4E               dec esi
004045CA 75E6             jnz $004045b2

As far as I can understand, it calls High() function again and again in the loop:

Project3.dpr.13: if (i = High(arr)) then
    004045B2 8B45FC           mov eax,[ebp-$04]
    004045B5 E8E6FAFFFF       **call @DynArrayHigh**
    004045BA 3BD8             cmp ebx,eax

When building configuration is set to RELEASE, the breakpoint isn't available, so I press F8/F7 to step into the loop.

00404589 6A64             push $64
0040458B 8D45FC           lea eax,[ebp-$04]
0040458E B901000000       mov ecx,$00000001
00404593 8B1554454000     mov edx,[$00404554]
00404599 E8B6FCFFFF       call @DynArraySetLength
0040459E 83C404           add esp,$04
004045A1 8B45FC           mov eax,[ebp-$04]
004045A4 E8F7FAFFFF       call @DynArrayHigh
004045A9 8BF0             mov esi,eax
004045AB 85F6             test esi,esi
004045AD 7C1D             jl $004045cc
004045AF 46               inc esi
004045B0 33DB             xor ebx,ebx
004045B2 8B45FC           mov eax,[ebp-$04]
004045B5 E8E6FAFFFF       call @DynArrayHigh
004045BA 3BD8             cmp ebx,eax
004045BC 750A             jnz $004045c8
004045BE 8B45FC           mov eax,[ebp-$04]
004045C1 C70498F7FFFFFF   mov [eax+ebx*4],$fffffff7
004045C8 43               inc ebx
004045C9 4E               dec esi
004045CA 75E6             jnz $004045b2
004045CC 33C0             xor eax,eax
004045BC 750A             jnz $004045c8

Again, the same call @DynArrayHigh is produced...
So my question is, why the compiler can't optimize this? simply save the High() value in a local register/variable because the array size is not changed.

like image 489
justyy Avatar asked Mar 13 '13 13:03

justyy


1 Answers

This is not a answer but rather a (self destructing) comment :)

In my view, The compiler must not attempt to optimize this.

Why should the compiler attempt to optimize a (non-deterministic) High function as opposed to other? (such as Length)

The dynamic array length might be changed inside the loop either by SetLenth, or by other means. the array might be re-initilzed at run-time and your code might depend on that:

for i := 0 to High(arr) do
begin
  if (i = High(arr)) then
    arr[i] := -9
  else
    if foo() then
      arr := nil; // or SetLength(arr, 0);

  if High(arr) = -1 then Exit; // arr is nil  
end;

How do you suggest this should be optimized? Should the compiler even attempt to optimized this? I don't see anything special about High functionm even if the compiler translates it to @DynArrayHigh.

If you want your code to be optimized , optimize it yourself .e.g:

var
  arrHigh: Integer;

  arrHigh := High(arr);
  for i := 0 to arrHigh do
    if i = arrHigh then...
like image 107
kobik Avatar answered Sep 19 '22 16:09

kobik