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

compiler-construction, compiler-optimization, delphi

Solution

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...

Problem

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.

Original source