Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Code Generation/Optimization    [ Add a report in this area ]  
Report #:  121566   Status: Closed
[REGRESSION] Monstrous regression of code-generator for inline function
Project:  Delphi Build #:  XE2, ... XE5
Version:    19.0 Submitted By:   Alexey Kazantsev
Report Type:  Feature Specification issue Date Reported:  1/5/2014 2:19:53 PM
Severity:    Serious / Highly visible problem Last Updated: 4/15/2014 6:33:49 PM
Platform:    All versions Internal Tracking #:   46418
Resolution: Fixed (Resolution Comments) Resolved in Build: : XE6
Duplicate of:  None
Voting and Rating
Overall Rating: (6 Total Ratings)
5.00 out of 5
Total Votes: 76
Description
Compare generated code for call of two inlined class functions:

Delphi 2006 - XE:
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0040915A 8B1524E24000     mov edx,[$0040e224]
00409160 A120E24000       mov eax,[$0040e220]
00409165 E8F2C7FFFF       call CompareStr
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0040916A 8B1524E24000     mov edx,[$0040e224]
00409170 A120E24000       mov eax,[$0040e220]
00409175 E8E2C7FFFF       call CompareStr
0040917A 85C0             test eax,eax
0040917C 0F94C0           setz al

Delphi XE2 Update 4 (16.0.4429.46931):
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0041B3AB 8D45EC           lea eax,[ebp-$14]
0041B3AE 8B15D82E4200     mov edx,[$00422ed8]
0041B3B4 E84BA4FEFF       call @UStrLAsg
0041B3B9 8D45E8           lea eax,[ebp-$18]
0041B3BC 8B15DC2E4200     mov edx,[$00422edc]
0041B3C2 E83DA4FEFF       call @UStrLAsg
0041B3C7 8B55E8           mov edx,[ebp-$18]
0041B3CA 8B45EC           mov eax,[ebp-$14]
0041B3CD E8F28FFFFF       call CompareStr
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0041B3D2 8D45E4           lea eax,[ebp-$1c]
0041B3D5 8B15D82E4200     mov edx,[$00422ed8]
0041B3DB E824A4FEFF       call @UStrLAsg
0041B3E0 8D45E0           lea eax,[ebp-$20]
0041B3E3 8B15DC2E4200     mov edx,[$00422edc]
0041B3E9 E816A4FEFF       call @UStrLAsg
0041B3EE 8D45DC           lea eax,[ebp-$24]
0041B3F1 8B55E4           mov edx,[ebp-$1c]
0041B3F4 E80BA4FEFF       call @UStrLAsg
0041B3F9 8D45D8           lea eax,[ebp-$28]
0041B3FC 8B55E0           mov edx,[ebp-$20]
0041B3FF E800A4FEFF       call @UStrLAsg
0041B404 8B55D8           mov edx,[ebp-$28]
0041B407 8B45DC           mov eax,[ebp-$24]
0041B40A E8B58FFFFF       call CompareStr
0041B40F 85C0             test eax,eax
0041B411 0F94C0           setz al

Delphi XE5 Update 2 (19.0.14356.6604):
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0041B3F4 8D45EC           lea eax,[ebp-$14]
0041B3F7 8B15BC2E4200     mov edx,[$00422ebc]
0041B3FD E85EACFEFF       call @UStrLAsg
0041B402 8D45E8           lea eax,[ebp-$18]
0041B405 8B15C02E4200     mov edx,[$00422ec0]
0041B40B E850ACFEFF       call @UStrLAsg
0041B410 33C0             xor eax,eax
0041B412 55               push ebp
0041B413 684AB44100       push $0041b44a
0041B418 64FF30           push dword ptr fs:[eax]
0041B41B 648920           mov fs:[eax],esp
0041B41E 8B55E8           mov edx,[ebp-$18]
0041B421 8B45EC           mov eax,[ebp-$14]
0041B424 E8DB79FFFF       call CompareStr
0041B429 8945CC           mov [ebp-$34],eax
0041B42C 33C0             xor eax,eax
0041B42E 5A               pop edx
0041B42F 59               pop ecx
0041B430 59               pop ecx
0041B431 648910           mov fs:[eax],edx
0041B434 6851B44100       push $0041b451
0041B439 8D45EC           lea eax,[ebp-$14]
0041B43C E803A9FEFF       call @UStrClr
0041B441 8D45E8           lea eax,[ebp-$18]
0041B444 E8FBA8FEFF       call @UStrClr
0041B449 C3               ret
0041B44A E901A0FEFF       jmp @HandleFinally
0041B44F EBE8             jmp $0041b439
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0041B451 8D45E4           lea eax,[ebp-$1c]
0041B454 8B15BC2E4200     mov edx,[$00422ebc]
0041B45A E801ACFEFF       call @UStrLAsg
0041B45F 8D45E0           lea eax,[ebp-$20]
0041B462 8B15C02E4200     mov edx,[$00422ec0]
0041B468 E8F3ABFEFF       call @UStrLAsg
0041B46D 8D45DC           lea eax,[ebp-$24]
0041B470 E8CFA8FEFF       call @UStrClr
0041B475 8D45D8           lea eax,[ebp-$28]
0041B478 E8C7A8FEFF       call @UStrClr
0041B47D 8D45D4           lea eax,[ebp-$2c]
0041B480 E8BFA8FEFF       call @UStrClr
0041B485 8D45D0           lea eax,[ebp-$30]
0041B488 E8B7A8FEFF       call @UStrClr
0041B48D 33C0             xor eax,eax
0041B48F 55               push ebp
0041B490 6838B54100       push $0041b538
0041B495 64FF30           push dword ptr fs:[eax]
0041B498 648920           mov fs:[eax],esp
0041B49B 8D45DC           lea eax,[ebp-$24]
0041B49E 8B55E4           mov edx,[ebp-$1c]
0041B4A1 E8BAABFEFF       call @UStrLAsg
0041B4A6 8D45D8           lea eax,[ebp-$28]
0041B4A9 8B55E0           mov edx,[ebp-$20]
0041B4AC E8AFABFEFF       call @UStrLAsg
0041B4B1 33C0             xor eax,eax
0041B4B3 55               push ebp
0041B4B4 68EBB44100       push $0041b4eb
0041B4B9 64FF30           push dword ptr fs:[eax]
0041B4BC 648920           mov fs:[eax],esp
0041B4BF 8B55D8           mov edx,[ebp-$28]
0041B4C2 8B45DC           mov eax,[ebp-$24]
0041B4C5 E83A79FFFF       call CompareStr
0041B4CA 8945C4           mov [ebp-$3c],eax
0041B4CD 33C0             xor eax,eax
0041B4CF 5A               pop edx
0041B4D0 59               pop ecx
0041B4D1 59               pop ecx
0041B4D2 648910           mov fs:[eax],edx
0041B4D5 68F2B44100       push $0041b4f2
0041B4DA 8D45DC           lea eax,[ebp-$24]
0041B4DD E862A8FEFF       call @UStrClr
0041B4E2 8D45D8           lea eax,[ebp-$28]
0041B4E5 E85AA8FEFF       call @UStrClr
0041B4EA C3               ret
0041B4EB E9609FFEFF       jmp @HandleFinally
0041B4F0 EBE8             jmp $0041b4da
0041B4F2 837DC400         cmp dword ptr [ebp-$3c],$00
0041B4F6 0F9445CB         setz byte ptr [ebp-$35]
0041B4FA 33C0             xor eax,eax
0041B4FC 5A               pop edx
0041B4FD 59               pop ecx
0041B4FE 59               pop ecx
0041B4FF 648910           mov fs:[eax],edx
0041B502 683FB54100       push $0041b53f
0041B507 8D45E4           lea eax,[ebp-$1c]
0041B50A E835A8FEFF       call @UStrClr
0041B50F 8D45E0           lea eax,[ebp-$20]
0041B512 E82DA8FEFF       call @UStrClr
0041B517 8D45DC           lea eax,[ebp-$24]
0041B51A E825A8FEFF       call @UStrClr
0041B51F 8D45D8           lea eax,[ebp-$28]
0041B522 E81DA8FEFF       call @UStrClr
0041B527 8D45D4           lea eax,[ebp-$2c]
0041B52A E815A8FEFF       call @UStrClr
0041B52F 8D45D0           lea eax,[ebp-$30]
0041B532 E80DA8FEFF       call @UStrClr
0041B537 C3               ret
0041B538 E9139FFEFF       jmp @HandleFinally
0041B53D EBC8             jmp $0041b507
-----------------------------------------------------------------
Steps to Reproduce:
--------
program inline_bug;

{$APPTYPE CONSOLE}

{$OPTIMIZATION ON}
{$INLINE ON}

{$R *.res}

uses
  SysUtils;

Type

Utils = Class

  Type

   Namespace = Class

    Class Function CompareString(Const ALeft, ARight : String) : Integer; Static; Inline;
    Class Function CompareText(Const ALeft, ARight : String) : Integer; Static; Inline;

    Class Function SameString(Const ALeft, ARight : String; ACaseSensitive : Boolean = True) : Boolean; Static; Inline;

   End;

End;

{ Utils.NameSpace }

//
Class Function Utils.Namespace.CompareString(const ALeft, ARight : System.String) : Integer;
Begin

Result := SysUtils.CompareStr(ALeft, ARight);

End;
//

//
Class Function Utils.Namespace.CompareText(Const ALeft, ARight : String) : Integer;
Begin

Result := SysUtils.CompareText(ALeft, ARight);

End;
//

//
Class Function Utils.Namespace.SameString(Const ALeft, ARight : String; ACaseSensitive : Boolean) : Boolean;
begin

If ACaseSensitive Then
  Result := Utils.Namespace.CompareString(ALeft, ARight) = 0
Else
  Result := Utils.Namespace.CompareText(ALeft, Aright) = 0;

End;
//

Var

s1, s2 : String;

begin

Utils.Namespace.CompareString(S1, S2);
Utils.Namespace.SameString(S1, S2);
s1 := '';

end.
--------
Workarounds
None
Attachment
inline_bug.zip
Comments

Dmitry Burov at 2/3/2014 6:24:14 AM -
Just an example of inline class operator implicit for the record, splitting Int32 to 4 bytes and back.

http://synopse.info/forum/viewtopic.php?pid=3256#p3256

=======

talking about performance, how would you like this ? good, it is legacy compatibility bridge. Pure insanity....
Main.pas.49: lblJvIPAddressValuesAddress.Caption := IntToStr(AddressValues.Address);
00563B20 8B45F8           mov eax,[ebp-$08]
00563B23 8945E0           mov [ebp-$20],eax
00563B26 8B45E0           mov eax,[ebp-$20]
00563B29 8945F4           mov [ebp-$0c],eax
00563B2C 8B45F4           mov eax,[ebp-$0c]
...
Main.pas.50: lblJvIPAddressValuesValue1.Caption := IntToStr(AddressValues.Value1);
00563B52 8B45F8           mov eax,[ebp-$08]
00563B55 8945DC           mov [ebp-$24],eax
00563B58 8B45DC           mov eax,[ebp-$24]
00563B5B 8945F0           mov [ebp-$10],eax
00563B5E 8B45F0           mov eax,[ebp-$10]
00563B61 8945D4           mov [ebp-$2c],eax
00563B64 8B45D4           mov eax,[ebp-$2c]
00563B67 BA04000000       mov edx,$00000004
===========

Another nice feat of XE2 - it no more understands that "(integer, cardinal) div (2,4,8)" is but a bitshift and makes full-featured division, which if nothing else exhausts registers frame, scarce in x86 )prev. post at the given link

Alexey Kazantsev at 3/10/2014 2:23:34 PM -
This bug is reproduced also for dynamic arrays and interfaces. More simple code for reproducing:

program inline_bug;

{$APPTYPE CONSOLE}

{$OPTIMIZATION ON}
{$INLINE ON}

{$R *.res}

Type

TManagedType = String; // checked with: String, IInterface, TBytes and record with field of one of these types;

//
Function Func(Const ALeft, ARight : TManagedType) : Boolean;
Begin

Result := True;

End;
//

//
Function CompareVars(Const ALeft, ARight : TManagedType) : Boolean; Inline;
Begin
End;
//

//
Function CompareVarsAlt(Const ALeft, ARight : TManagedType) : Boolean; Inline;
Begin

Result := Func(ALeft, ARight);

End;
//

//
Function SameVars(Const ALeft, ARight : TManagedType; AAlternativeWay : Boolean = True) : Boolean; Inline;
begin

If AAlternativeWay Then
  Result := CompareVarsAlt(ALeft, ARight)
Else
  Result := CompareVars(ALeft, ARight);

End;
//

Var

v1, v2 : TManagedType;

begin

CompareVarsAlt(v1, v2);
SameVars(v1, v2);
v1 := Default(TManagedType);

end.

Server Response from: ETNACODE01