Wrong offsets in array of strings of size 5 or 11 when peephole optimizer enabled for 80386 CPU

Summary

When accessing elements in an array of string[5] or string[11], byte offsets from the base pointer of the array are calculated wrong.

System Information

  • Operating system: Go32v2 and 32-bit Windows.
  • Processor architecture: x86
  • Compiler version: 3.x
  • Device: Computer

Steps to reproduce

Compile the example project. For Go32v2:

fpc -Tgo32v2 -O1 -Op80386 -Oopeephole test.pas

For 32-bit Windows:

fpc -Twin32 -O1 -Op80386 -Oopeephole test.pas

It is important to enable peephole optimization and set CPU to 80386.

Example project

const
  StrLen = 11 {5 or 11};
  Str: array [0..15] of string[StrLen] = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15');
var
  I: Integer;
  S: ^string;
begin
  WriteLn('@Str=', HexStr(PtrUInt(@Str), 8));
  for I := Low(Str) to High(Str) div 4 do
  begin
    {$IFDEF CPU16}
      asm jmp @1; nop; int3; @1: end;
    {$ELSE}
      asm jmp .L1; nop; int3; .L1: end;
    {$ENDIF}
    S := @Str[I];
    WriteLn('Str[', I, ']=@', HexStr(PtrUInt(S), 8), '=@Str+', PtrUInt(S) - PtrUInt(@Str), '=''', S^, '''');
  end;
end.

What is the current bug behavior?

For string[5] (array element size of 6 bytes), index is multiplied by 10 to get offset. For string[11] (12 bytes), index is multiplied by 36.

What is the expected (correct) behavior?

Index should be multipled by element size to get byte offset. Behavior is correct in FPC 2.6.4; also in FPC 3.x for i8086.

Relevant logs and/or screenshots

For string[5]:

@Str=004090D0
Str[0]=@004090D0=@Str+0='0'
Str[1]=@004090DA=@Str+10=' 2    3    4    5    6    '
Str[2]=@004090E4=@Str+20=''
Str[3]=@004090EE=@Str+30='5'

For string[11]:

@Str=004090D0
Str[0]=@004090D0=@Str+0='0'
Str[1]=@004090F4=@Str+36='3'
Str[2]=@00409118=@Str+72='6'
Str[3]=@0040913C=@Str+108='9'

Look for bytes 0x90 0xCC in the compiled code to see what happens. Multiplication of eax by 6 is converted to:

8D 04 00               lea   eax,[eax][eax]
8D 04 80               lea   eax,[eax][eax]*4

which is actually multiplication by 10. Multiplication of eax by 12 becomes:

8D 04 85 00 00 00 00   lea   eax,[eax]*4[0]
8D 04 C0               lea   eax,[eax][eax]*8

that is, multiplication by 36. In FPC 2.6.4, these compiled correctly to:

8D 04 40               lea   eax,[eax][eax]*2
01 C0                  add   eax,eax

and

8D 04 40               lea   eax,[eax][eax]*2
8D 04 85 00 00 00 00   lea   eax,[eax]*4[0]

Possible fixes

Probably, the peephole optimizer optimizes multiplications by 6 and 12 incorrectly. In file \fpc-3.2.2\compiler\i386\aoptcpu.pas of the source, the replacement seems to be correct; the multiplication of an integer variable by a constant of 6 or 12 is also correct. Maybe, array offset calculation is using another code path?

Edited by JoeForsterSTA