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