Skip to content

inlined function does not produce same result as normal function

Summary

Normal function produces expected result, same function but inlined returns different and wrong result.

System Information

  • Operating system: Linux_DEBIAN_bookworm(12)
  • Processor architecture: x86-64
  • Compiler version: Free Pascal Compiler version 3.2.2+dfsg-20 [2023/03/30] for x86_64
  • Device: Computer
  • Compiler Optimization Level: None (0)

Steps to reproduce

The example provided is full pascal program. Just compile and run. The inline test will fail with assertion error.

Example Project (64-bit only)

program inline_problem;

  {$mode DELPHI}

  {$DEFINE DEBUG}
  {$ASSERTIONS ON}

type
  TTest = class sealed
  strict private
  type

    TFoobar = packed record
      A: uint16;
      B: uint16;
      C: uint16;
    end;

    TA = packed record
      Foobar: TFoobar;
      R: uint16;
      procedure Print;
    end;

    TB = packed record
      R: uint16;
      Foobar: TFoobar;
      procedure Print;
    end;

  strict private
    class function Convert_A_into_B(const A: Pointer): Pointer; static;
    class function Convert_A_into_B_INLINED(const A: Pointer): Pointer; inline; static;
  public
    class procedure PerformNormalTest; static;
    class procedure PerformInlinedTest; static;
  end;


  class function TTest.Convert_A_into_B(const A: Pointer): Pointer;
  var
    LA: TA absolute A;
    LB: TB absolute Result;
  begin
    LB.R := LA.R;
    LB.Foobar.A := LA.Foobar.A;
    LB.Foobar.B := LA.Foobar.B;
    LB.Foobar.C := LA.Foobar.C;
  end;

  class function TTest.Convert_A_into_B_INLINED(const A: Pointer): Pointer;
  var
    LA: TA absolute A;
    LB: TB absolute Result;
  begin
    LB.R := LA.R;
    LB.Foobar.A := LA.Foobar.A;
    LB.Foobar.B := LA.Foobar.B;
    LB.Foobar.C := LA.Foobar.C;
  end;

  class procedure TTest.PerformNormalTest;
  var
    A: TObject;
    B: Pointer;
  begin
    A := TObject.Create;
    B := nil;
    try
      Writeln;
      Writeln('Normal Test');
      Writeln('-----------');
      TA(A).Print;
      Writeln;
      B := Convert_A_into_B(A);
      TB(B).Print;
      Writeln('-----------');
      Assert(TA(A).Foobar.A = TB(B).Foobar.A, 'A values do not match');
      Assert(TA(A).Foobar.B = TB(B).Foobar.B, 'B values do not match');
      Assert(TA(A).Foobar.C = TB(B).Foobar.C, 'C values do not match');
      Writeln('Everything ok');
    finally
      A.Free;
    end;
  end;

  class procedure TTest.PerformInlinedTest;
  var
    A: TObject;
    B: Pointer;
  begin
    A := TObject.Create;
    B := nil;
    try
      Writeln;
      Writeln('INLINE Test');
      Writeln('-----------');
      TA(A).Print;
      Writeln;
      B := Convert_A_into_B_INLINED(A);
      TB(B).Print;
      Writeln('-----------');
      Assert(TA(A).Foobar.A = TB(B).Foobar.A, 'A values do not match');
      Assert(TA(A).Foobar.B = TB(B).Foobar.B, 'B values do not match');
      Assert(TA(A).Foobar.C = TB(B).Foobar.C, 'C values do not match');
      Writeln('Everything ok');
    finally
      A.Free;
    end;
  end;

  procedure TTest.TA.Print;
  begin
    Writeln('A = ', Foobar.A);
    Writeln('B = ', Foobar.B);
    Writeln('C = ', Foobar.C);
    Writeln('R = ', R);
  end;

  procedure TTest.TB.Print;
  begin
    Writeln('A = ', Foobar.A);
    Writeln('B = ', Foobar.B);
    Writeln('C = ', Foobar.C);
    Writeln('R = ', R);
  end;

begin

  TTest.PerformNormalTest;
  TTest.PerformInlinedTest;

end.

What is the expected (correct) behavior?

The result of inlined function should be the same as that of normal function.

Relevant logs and/or screenshots

./inline_problem 

Normal Test
-----------
A = 28736
B = 35360
C = 32527
R = 0

A = 28736
B = 35360
C = 32527
R = 0
-----------
Everything ok

INLINE Test
-----------
A = 28736
B = 35360
C = 32527
R = 0

A = 0
B = 0
C = 0
R = 0
-----------
A values do not match (inline_problem.lpr, line 103).
Edited by Peter V
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information