logo
  • Buy
  • Download
  • Documentation
  • Blog
  • Contact

24 Apr 2017

FixInsight and the inline directive

by Roman Yankovsky 1 Comment

The Delphi compiler allows functions and procedures to be tagged with the inline directive to improve performance. If the function or procedure meets certain criteria, the compiler will insert code directly, rather than generating a call. Embarcadero docwiki gives a list of conditions under which inlining does or does not occur.

One of basic conditions says: within a unit, the body for an inline function should be defined before calls to the function are made.

FixInsight 2017.04 introduces rule O805 “Inline marked routine comes after its call in the same unit”. Let’s check FMX and VCL code to see if Embarcadero follows their own rules. Short answer: it doesn’t.

I will give a couple of examples from Delphi 10.1 Berlin (version 24.0.22858.6822).

procedure TWinControl.ReadState(Reader: TReader);
begin
  DisableAlign;
  try
    inherited ReadState(Reader);
  finally
    EnableAlign;
  end;
  FixupTabList;
  if FParent <> nil then Perform(CM_PARENTCTL3DCHANGED, 0, 0);
  UpdateControlState;
end;
procedure TWinControl.AlignControl(AControl: TControl);
var
  Rect: TRect;
begin
  if not HandleAllocated or (csDestroying in ComponentState) then Exit;
  if FAlignLevel <> 0 then
    Include(FControlState, csAlignmentNeeded)
  else
  begin
    DisableAlign;
    try
      Rect := GetClientRect;
      AlignControls(AControl, Rect);
    finally
      Exclude(FControlState, csAlignmentNeeded);
      EnableAlign;
    end;
  end;
end;
procedure TWinControl.DisableAlign;
begin
  Inc(FAlignLevel);
end;

TWinControl.DisableAlign is an inline marked procedure. It is called at line 8531 and line 9019, but its body is defined after the calls – at line 9030. Obviously, this function will not be inlined.

One more example from another unit:

procedure TListViewBase.UpdateDeleteButtonLayout;
var
  RelRect: TRectF;
begin
  if (Adapter.Count < 1) or (FDeleteLayout = nil) or ((FDeleteButtonIndex = -1) and
    (FPrevDeleteButtonIndex = -1)) then
    Exit;

  if (FListingService <> nil) and (TListingTransitionFeature.DeleteButtonSlide in FListingService.GetTransitionFeatures) then
  begin
    FDeleteLayout.Width := DefaultDeleteButtonWidth * FDeleteModeTransitionAlpha;
    FDeleteButton.Opacity := 1;
  end
  else
  begin
    if FDeleteModeTransitionAlpha > 0 then
      FDeleteLayout.Width := DefaultDeleteButtonWidth
    else
      FDeleteLayout.Width := 0;

    FDeleteButton.Opacity := 0.5 + (FDeleteModeTransitionAlpha / 2);
  end;

  FDeleteLayout.Height := GetItemHeight(FDeleteButtonIndex);
  FDeleteLayout.Position.X := Width - FDeleteLayout.Width;

  if FDeleteButtonIndex = -1 then
    RelRect := GetItemRelRect(FPrevDeleteButtonIndex, LocalRect)
  else
    RelRect := GetItemRelRect(FDeleteButtonIndex, LocalRect);

  FDeleteLayout.Position.Y := (RelRect.Top + RelRect.Bottom - FDeleteLayout.Height) / 2;
end;

The method above contains two GetItemRelRect calls (lines 2457 and 2459), but both are before the actual GetItemRelRect body position in that unit (line 2868):

function TListViewBase.GetItemRelRect(const Index: Integer; const LocRect: TRectF;
  const SideSpace: Integer = 0): TRectF;
begin
  Result := RectF(LocRect.Left + FSideSpace + SideSpace, LocRect.Top + FSideSpace + FHeightSums[Index] - FScrollViewPos,
    LocRect.Width - ((SideSpace + FSideSpace) * 2), GetItemHeight(Index));

  if (FScrollBar <> nil) and (not HasTouchTracking) and FScrollBar.Visible then
    Result.Right := Result.Right - FScrollBar.Width;
end;

Despite being declared as inline, this method will not be inlined.

It is not a critical issue, but this makes inline directive useless. There are more occurrences of this issue in Vcl.ExtActns.pas, FMX.ASE.Lexer.pas, FMX.Graphics.pas, FMX.Types.pas, FMX.Utils.pas and FMX.ZOrder.Win.pas.

This means that inlining conditions are not easy to follow, even though at first glance inline directive seems to be an easy way to slightly optimize your code. FixInsight may help to make inlining more useful by avoiding such mistakes.

You can download FixInsight trial and check your own code.

P.S. Thanks to Stefan Glienke for suggesting this new FixInsight rule.

24 Apr 2017

FixInsight 2017.04 support Delphi 10.2 Tokyo

by Roman Yankovsky Leave a comment

I’m happy to announce FixInsight 2017.04 release with full Delphi 10.2 Tokyo support.

What’s New

  • New rule: O805 Inline marked routine comes after its call in the same unit
  • New rule: O804 Method parameter is declared but never used
  • Delphi 10.2 Tokyo support
  • Improved parser
  • Fixed false positives in some cases
  • Performance improvements

http://www.tmssoftware.com/site/fixinsight.asp

  • Announcements
  • DelphiAST
  • DelphiSpec
  • FixInsight
  • FMX
  • Other
  • VCL

Recent Posts

  • Find leaks in Delphi with Deleaker
  • FixInsight and the inline directive
  • FixInsight 2017.04 support Delphi 10.2 Tokyo
  • FixInsight 2016.04 support Delphi 10.1 Berlin
  • FixInsight vs FMX in Delphi 10.1 Berlin

Archives

  • January 2020
  • April 2017
  • April 2016
  • March 2016
  • December 2015
  • November 2015
  • October 2015
  • September 2015
  • August 2015
  • April 2015
  • March 2015
  • February 2015
  • September 2014
  • August 2014
  • January 2014
  • December 2013
  • October 2013

Recent Comments

  • anapa-poseidon3.ru on FixInsight vs RTL
  • Heat pump on FixInsight vs RTL
  • Suing on FixInsight vs RTL
  • JorgeJag on Find leaks in Delphi with Deleaker
  • Prorabdom on FixInsight vs RTL
  • Home
  • Buy
  • Download
  • Documentation
  • Blog
  • Contact
  • © 2014-2015 SourceOddity|
  • Terms and Conditions|
  • Privacy Policy