View Issue Details

IDProjectCategoryView StatusLast Update
0034743FPCRTLpublic2019-01-05 22:02
ReportersilvioprogAssigned ToSven Barth 
PrioritynormalSeverityminorReproducibilityhave not tried
Status assignedResolutionopen 
Platformx86_64OSLinuxOS Version
Product Version3.3.1Product Build 
Target VersionFixed in Version 
Summary0034743: [PATCH] Low-level invoke implementation for Linux x86_64
DescriptionHi.

The attached files are:

invoke_unix64.inc - the assembly to init the TFunctionCallManager on Linux 64 (System V ABI)
tests.pp - all tests for the implementation

This feature was suggested by Sven Barth at: http://lists.freepascal.org/pipermail/fpc-pascal/2018-October/054933.html
Steps To ReproduceJust open and run the tests.
Additional InformationNOTE 1: Sorry for not sending a GIT or SVN patch, I'm not sure which directory these files should be placed. Feel free to show me! :-)

NOTE 2: There is no tests calling constructors (maybe not supported yet).

Improvements/suggestions are welcome.

Merry Christmas!
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files
  • invoke_unix64.inc (10,172 bytes)
    {
      This file is part of the Free Pascal run time library.
      Copyright (C) 2018 Silvio Clecio (silvioprog) member of the
      Free Pascal development team.
    
      Low-level invoke implementation for Linux x86_64
    
      See the file COPYING.FPC, included in this distribution,
      for details about the copyright.
    
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    }
    
    {$IF DEFINED(CPUX86_64) AND DEFINED(UNIX)}
     {$DEFINE SYSTEM_HAS_INVOKE}
    {$ENDIF}
    
    {$IFDEF SYSTEM_HAS_INVOKE}
    
    const
      FPREGS_COUNT = 8;
      GPREGS_COUNT = 6;
    
      INTEGER_CLASS = 0;
      SSE_CLASS = 1;
      X87_CLASS = 2;
      MEMORY_CLASS = 3;
    
    type
      TStackItem = packed record
        Value: PtrUInt; // 0-8
        Size: SizeInt; // 8-16
        ByRef: Boolean; // 16-17
        ArgCls: Byte; // 17-18
      end;
    
      TResultType = type Byte;
    
      TInvokeHandle = packed record
        FPRegs: array[0..Pred(FPREGS_COUNT)] of PtrUInt; // 0-64
        GPRegs: array[0..Pred(GPREGS_COUNT)] of PtrUInt; // 64-112
        StackAddr: Pointer; // 112-120
        StackCount: PtrUInt; // 120-128
        StackSize: SizeUInt; // 128-136
        FuncAddr: CodePointer; // 136-144
        ResultType: TResultType; // 1144-145
        Result: PtrUInt; // 145-153
      end;
    
    procedure InvokeKernelUnix64(var AHandle: TInvokeHandle); assembler; nostackframe;
    label
      get_stack_item, check_stack_items, get_extra_arg, check_extra_args, by_ref,
        mem_cls, exit;
    asm
      { save base pointer; set new base pointer to rsp }
      push %rbp
      mov %rsp, %rbp
    
      { save callee-saved registers }
      push %rbx
      push %r12
      push %r13
      push %r14
      push %r15
    
      { save invoke handle }
      mov %rdi, %r15
    
      { allocate extra arguments space }
      mov 128(%r15), %rcx // Handle.StackSize
      sub %rcx, %rsp
    
      { iterate stack items }
      mov 112(%r15), %rsi // Handle.StackAddr
      mov $-8, %r13
      mov $-18, %rax
      mov 120(%r15), %rcx // Handle.StackCount
      jmp check_stack_items
    get_stack_item:
      add $18, %rax
      lea (%rsi, %rax), %rdi
      mov (%rdi), %r9 // Item.Value
      mov 8(%rdi), %r8 // Item.Size
      movb 16(%rdi), %r12b // Item.ByRef
      movb 17(%rdi), %dl // Item.ArgCls
      { get stack item values }
      mov $-8, %r14
      jmp check_extra_args
    get_extra_arg:
      add $8, %r13
      add $8, %r14
      cmpb $1, %r12b
      je by_ref
      { INTEGER/SSE CLASS }
      lea (%r9, %r14), %rbx
      mov %rbx, (%rsp, %r13)
      dec %rcx
      jmp check_stack_items
    by_ref:
      cmpb MEMORY_CLASS, %dl
      je mem_cls
      { POINTER/X87 CLASS }
      mov (%r9, %r14), %rbx
      mov %rbx, (%rsp, %r13)
      sub $8, %r8
      jmp check_extra_args
    mem_cls:
      { MEMORY CLASS }
      sub $8, %r8
      mov (%r9, %r14), %rbx
      mov %rbx, (%rsp, %r13)
      cmp $0, %r8
      jg get_extra_arg
      dec %rcx
      jmp check_stack_items
    check_extra_args:
      cmp $0, %r8
      jge get_extra_arg
      dec %rcx
    check_stack_items:
      cmp $0, %rcx
      jnz get_stack_item
    
      { setup general purpose registers }
      lea 64(%r15), %rbx // Handle.GPRegs
      mov (%rbx), %rdi
      mov 8(%rbx), %rsi
      mov 16(%rbx), %rdx
      mov 24(%rbx), %rcx
      mov 32(%rbx), %r8
      mov 40(%rbx), %r9
    
      { setup floating point registers }
      lea (%r15), %rbx // Handle.FPRegs
      movq (%rbx), %xmm0
      movq 8(%rbx), %xmm1
      movq 16(%rbx), %xmm2
      movq 24(%rbx), %xmm3
      movq 32(%rbx), %xmm4
      movq 40(%rbx), %xmm5
      movq 48(%rbx), %xmm6
      movq 56(%rbx), %xmm7
    
      { invoke user function }
      mov $0, %rax
      call *136(%r15) // Handle.FuncAddr
    
      { get result type }
      movb 144(%r15), %cl // Handle.ResultType
    
      { return 64-bit value }
      mov %rax, 145(%r15) // Handle.Result
      cmpb INTEGER_CLASS, %cl
      je exit
    
      { return 80-bit float }
      cmpb X87_CLASS, %cl
      jne exit
      fstpt 145(%r15) // Handle.Result
    
    exit:
    
      { deallocate extra arguments space }
      mov 128(%r15), %rcx // Handle.StackSize
      add %rcx, %rsp
    
      { restore callee-saved registers }
      pop %r15
      pop %r14
      pop %r13
      pop %r12
      pop %rbx
    
      { reset stack to base pointer; restore old base pointer }
      leave
      { return to caller }
      ret
    end;
    
    {$ENDIF}
    
    resourcestring
    {$IFDEF SYSTEM_HAS_INVOKE}
      SErrFailedToConvertArg = 'Failed to convert argument %d of type %s';
    {$ELSE}
      SErrPlatformNotSupported = 'Invoke is not supported on this platform';
    {$ENDIF}
    
    procedure CallManagerInvoke(AFuncAddr: CodePointer;
      const AArgs: TFunctionCallParameterArray; ACallConv: TCallConv;
      AResultType: PTypeInfo; AResultValue: Pointer; AFlags: TFunctionCallFlags);
    {$IFDEF SYSTEM_HAS_INVOKE}
    var
      VArg: TFunctionCallParameter;
      VHandle: TInvokeHandle;
      VStack: array of TStackItem;
      VRetReg, VRetSize: SizeInt;
      I, VGPRegIdx, VFPRegIdx: LongInt;
      VRetInParam: Boolean;
    
      function PushRegVal(AValue: PtrUInt;
        AArgCls: Byte = INTEGER_CLASS): Boolean;
      begin
        case AArgCls of
          INTEGER_CLASS:
          begin
            if VGPRegIdx = VRetReg then
              Inc(VGPRegIdx);
            if VGPRegIdx < GPREGS_COUNT then
            begin
              VHandle.GPRegs[VGPRegIdx] := AValue;
              Inc(VGPRegIdx);
              Inc(VHandle.StackSize, 8);
              Exit(True);
            end;
          end;
          SSE_CLASS:
            if VFPRegIdx < FPREGS_COUNT then
            begin
              VHandle.FPRegs[VFPRegIdx] := AValue;
              Inc(VFPRegIdx);
              Inc(VHandle.StackSize, 8);
              Exit(True);
            end;
        end;
        Result := False;
      end;
    
      procedure PushStackVal(AValue: PtrUInt; ASize: SizeInt;
        AByRef: Boolean = False; AArgCls: Byte = INTEGER_CLASS);
      var
        VStackItem: TStackItem;
      begin
        Inc(VHandle.StackCount);
        SetLength(VStack, VHandle.StackCount);
        VStackItem.ByRef := AByRef;
        VStackItem.Size := ASize;
        VStackItem.Value := AValue;
        VStackItem.ArgCls := AArgCls;
        VStack[Pred(VHandle.StackCount)] := VStackItem;
        if AArgCls = MEMORY_CLASS then
          Inc(VHandle.StackSize, ASize)
        else
          Inc(VHandle.StackSize, 16); // stack aligned on 16 bytes boundary
      end;
    
      function PushVal(AValue: PtrUInt; AArgCls: Byte = INTEGER_CLASS;
        AByRef: Boolean = False): Boolean; inline;
      begin
        Result := PushRegVal(AValue, AArgCls);
        if not Result then
          PushStackVal(AValue, 8, AByRef);
      end;
    
      function MapVal(ATD: PTypeData; AKind: TTypeKind;
        AValue: Pointer): Boolean;
      var
        VField: PManagedField;
        VTD: PTypeData;
        I: SizeUInt;
      begin
        Result := True;
        case AKind of
          tkInteger, tkEnumeration: PushVal(PtrUInt(AValue^));
          tkFloat:
            case ATD^.FloatType of
              ftSingle, ftDouble: PushVal(PtrUInt(AValue^), SSE_CLASS);
              ftExtended: PushStackVal(PtrUInt(AValue), 10, True);
              ftComp, ftCurr: PushVal(PtrUInt(AValue^));
            end;
          tkSet:
            if ATD^.SetSize <= 8 then
              PushVal(PtrUInt(AValue^))
            else
              if not PushRegVal(PtrUInt(AValue)) then
                PushStackVal(PtrUInt(AValue), ATD^.SetSize);
          tkMethod:
          begin
            PushRegVal(PtrUInt(AValue^));
            if not PushRegVal(PtrUInt(AValue^) + 8) then
              PushStackVal(PtrUInt(AValue), 8, True);
          end;
          tkSString: PushVal(PtrUInt(AValue));
          tkAString, tkWString, tkInterface, tkClass, tkObject, tkUString,
            tkDynArray, tkInterfaceRaw, tkProcVar, tkClassRef,
            tkPointer: PushVal(PtrUInt(AValue^));
          tkArray:
            if ATD^.ArrayData.Size <= 8 then
              PushVal(PtrUInt(AValue^))
            else
              if not PushRegVal(PtrUInt(AValue)) then
                PushStackVal(PtrUInt(AValue), ATD^.ArrayData.Size);
          tkRecord:
            if ATD^.RecSize <= 8 then
              PushVal(PtrUInt(AValue^))
            else
              if ATD^.RecSize <= 16 then
              begin
                VField := PManagedField(PByte(@ATD^.TotalFieldCount) + 4);
                for I := 0 to Pred(ATD^.TotalFieldCount) do
                begin
                  VTD := GetTypeData(VField^.TypeRef);
                  MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
                  if ((VGPRegIdx = GPREGS_COUNT) or (VFPRegIdx = FPREGS_COUNT)) and
                    (VHandle.StackCount = 0) then
                    MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
                  Inc(VField);
                end;
              end
              else
                PushStackVal(PtrUInt(AValue), ATD^.RecSize, True, MEMORY_CLASS);
          tkBool, tkInt64, tkQWord: PushVal(PtrUInt(AValue^));
        else
          Result := False;
        end;
      end;
    
    {$ENDIF}
    begin
    {$IFDEF SYSTEM_HAS_INVOKE}
      VRetInParam := False;
      if Assigned(AResultType) then
      begin
        if not Assigned(AResultValue) then
          raise EInvocationError.Create(SErrInvokeResultTypeNoValue);
        case AResultType^.Kind of
          tkSString, tkAString, tkWString, tkArray, tkInterface, tkDynArray,
            tkUString: VRetInParam := True;
          tkRecord: VRetInParam := GetTypeData(AResultType)^.RecSize > 8;
        end;
      end;
      VStack := nil;
      VHandle := Default(TInvokeHandle);
      VGPRegIdx := 0;
      VFPRegIdx := 0;
      if VRetInParam then
      begin
        if fcfStatic in AFlags then
          VRetReg := 0
        else
          VRetReg := 1;
        VHandle.GPRegs[VRetReg] := PtrUInt(AResultValue);
      end
      else
        VRetReg := -1;
      for I := 0 to High(AArgs) do
      begin
        VArg := AArgs[I];
        if VArg.Info.ParamFlags * [pfArray, pfOut, pfVar, pfConstRef] <> [] then
          PushVal(PtrUInt(VArg.ValueRef))
        else
          if not MapVal(GetTypeData(VArg.Info.ParamType),
            VArg.Info.ParamType^.Kind, VArg.ValueRef) then
            raise EInvocationError.CreateFmt(SErrFailedToConvertArg,
              [I, VArg.Info.ParamType^.Name]);
      end;
      VHandle.StackAddr := @VStack[0];
      VHandle.FuncAddr := AFuncAddr;
      if AResultType = TypeInfo(Extended) then
      begin
        VHandle.ResultType := X87_CLASS;
        VRetSize := 10;
      end
      else
      begin
        VHandle.ResultType := INTEGER_CLASS;
        VRetSize := 8;
      end;
      InvokeKernelUnix64(VHandle);
      if Assigned(AResultType) and not VRetInParam then
        Move(VHandle.Result, PPtrUInt(AResultValue)^, VRetSize);
    {$ELSE}
      raise EInvocationError.Create(SErrPlatformNotSupported);
    {$ENDIF}
    end;
    
    const
      CallManager: TFunctionCallManager = (
        Invoke: @CallManagerInvoke;
        CreateCallbackProc: nil;
        CreateCallbackMethod: nil;
        FreeCallback: nil;
      );
    
    procedure InitSystemFunctionCallManager;
    begin
      SetFunctionCallManager([ccReg, ccCdecl, ccPascal, ccStdCall], CallManager);
    end;
    
    invoke_unix64.inc (10,172 bytes)
  • tests.pp (59,385 bytes)
    program tests;
    
    {$MODE DELPHI}
    {$ASSERTIONS ON}
    {$WARN 4055 OFF}
    {$WARN 5024 OFF}
    {$WARN 5026 OFF}
    {$WARN 5079 OFF}
    
    uses
      SysUtils,
      Math,
      TypInfo,
      Rtti;
    
    {$I invoke_unix64.inc}
    
    type
      TArrayOfLongIntDyn = array of LongInt;
    
      TArrayOfLongIntStatic = array[0..2] of LongInt;
    
      TTestEnum = (te1, te2, te3, te4, te5, te6, te7, te8, te9, te10);
    
      TTestSet = set of TTestEnum;
    
      TTestProc = procedure;
    
      TTestFunc1 = function: LongInt;
    
      TTestFunc2 = function(AArg1: LongInt; AArg2: array of LongInt): string;
    
      TTestMethod1 = procedure of object;
    
      TTestMethodObj = class
      public
        procedure TestMethod1;
        procedure TestMethod2;
      end;
    
      TTestRecord1 = record
        Member1, Member2: LongInt;
      end;
    
      TTestRecord2 = record
        Member1: string;
        Member2: LongInt;
      end;
    
      TTestRecord3 = record
        Member1: LongInt;
        Member2: Double;
        Member3: string;
        Member4: Extended;
      end;
    
      TTestClass1 = class
        Field1, Field2: LongInt;
      end;
    
      TTestClass2 = class
        Field1: string;
        Field2: LongInt;
      end;
    
      TTestClass3 = class
        Field1: LongInt;
        Field2: Double;
        Field3: string;
        Field4: Extended;
      end;
    
      ITestIntf1 = interface
        function GetField1: LongInt;
        function GetField2: LongInt;
        procedure SetField1(AValue: LongInt);
        procedure SetField2(AValue: LongInt);
        property Field1: LongInt read GetField1 write SetField1;
        property Field2: LongInt read GetField2 write SetField2;
      end;
    
      TTestIntf1 = class(TInterfacedObject, ITestIntf1)
      private
        FField1: LongInt;
        FField2: LongInt;
        function GetField1: LongInt;
        function GetField2: LongInt;
        procedure SetField1(AValue: LongInt);
        procedure SetField2(AValue: LongInt);
      public
        property Field1: LongInt read GetField1 write SetField1;
        property Field2: LongInt read GetField2 write SetField2;
      end;
    
      ITestIntf2 = interface
        function GetField1: string;
        function GetField2: LongInt;
        procedure SetField1(const AValue: string);
        procedure SetField2(AValue: LongInt);
        property Field1: string read GetField1 write SetField1;
        property Field2: LongInt read GetField2 write SetField2;
      end;
    
      TTestIntf2 = class(TInterfacedObject, ITestIntf2)
      private
        FField1: string;
        FField2: LongInt;
        function GetField1: string;
        function GetField2: LongInt;
        procedure SetField1(const AValue: string);
        procedure SetField2(AValue: LongInt);
      public
        property Field1: string read GetField1 write SetField1;
        property Field2: LongInt read GetField2 write SetField2;
      end;
    
      ITestIntf3 = interface
        function GetField1: LongInt;
        function GetField2: Double;
        function GetField3: string;
        function GetField4: Extended;
        procedure SetField1(AValue: LongInt);
        procedure SetField2(AValue: Double);
        procedure SetField3(const AValue: string);
        procedure SetField4(AValue: Extended);
        property Field1: LongInt read GetField1 write SetField1;
        property Field2: Double read GetField2 write SetField2;
        property Field3: string read GetField3 write SetField3;
        property Field4: Extended read GetField4 write SetField4;
      end;
    
      TTestIntf3 = class(TInterfacedObject, ITestIntf3)
      private
        FField1: LongInt;
        FField2: Double;
        FField3: string;
        FField4: Extended;
        function GetField1: LongInt;
        function GetField2: Double;
        function GetField3: string;
        function GetField4: Extended;
        procedure SetField1(AValue: LongInt);
        procedure SetField2(AValue: Double);
        procedure SetField3(const AValue: string);
        procedure SetField4(AValue: Extended);
      public
        property Field1: LongInt read GetField1 write SetField1;
        property Field2: Double read GetField2 write SetField2;
        property Field3: string read GetField3 write SetField3;
        property Field4: Extended read GetField4 write SetField4;
      end;
    
    var
      GProcOK1: Boolean = False;
      GProcOK2: Boolean = True;
      GVal1: string = 'abc';
      GVal2: Integer = 123;
      GVal3: Double = 12.34;
    
    function EqF32(A, B: Single): Boolean; inline;
    begin
      Result := CompareValue(A, B) = 0;
    end;
    
    function EqF64(A, B: Double): Boolean; inline;
    begin
      Result := CompareValue(A, B) = 0;
    end;
    
    function EqF80(A, B: Extended): Boolean; inline;
    begin
      Result := CompareValue(A, B) = 0;
    end;
    
    function EqArr(A, B: array of LongInt): Boolean;
    var
      I: Integer;
    begin
      Result := Length(A) = Length(B);
      if Result then
        for I := Low(A) to High(A) do
          if A[I] <> B[I] then
            Exit(False);
      Result := True;
    end;
    
    procedure TestProcedure1;
    begin
      GProcOK1 := True;
    end;
    
    procedure TestProcedure2;
    begin
      GProcOK2 := False;
    end;
    
    function TestFunction1: LongInt;
    begin
      Result := 123;
    end;
    
    function TestFunction2: LongInt;
    begin
      Result := 456;
    end;
    
    function TestFunction3(AArg1: LongInt; AArg2: array of LongInt): string;
    var
      I: LongInt;
    begin
      Result := AArg1.ToString;
      for I in AArg2 do
        Result += I.ToString;
    end;
    
    function TestFunction4(AArg1: LongInt; AArg2: array of LongInt): string;
    var
      I: LongInt;
    begin
      Result := AArg1.ToString;
      for I in AArg2 do
        Result += I.ToString;
      Result += 'abc123';
    end;
    
    { TTestMethodObj }
    
    procedure TTestMethodObj.TestMethod1;
    begin
      GProcOK1 := True;
    end;
    
    procedure TTestMethodObj.TestMethod2;
    begin
      GProcOK2 := False;
    end;
    
    { TTestIntf1 }
    
    function TTestIntf1.GetField1: LongInt;
    begin
      Result := FField1;
    end;
    
    function TTestIntf1.GetField2: LongInt;
    begin
      Result := FField2;
    end;
    
    procedure TTestIntf1.SetField1(AValue: LongInt);
    begin
      FField1 := AValue;
    end;
    
    procedure TTestIntf1.SetField2(AValue: LongInt);
    begin
      FField2 := AValue;
    end;
    
    { TTestIntf2 }
    
    function TTestIntf2.GetField1: string;
    begin
      Result := FField1;
    end;
    
    function TTestIntf2.GetField2: LongInt;
    begin
      Result := FField2;
    end;
    
    procedure TTestIntf2.SetField1(const AValue: string);
    begin
      FField1 := AValue;
    end;
    
    procedure TTestIntf2.SetField2(AValue: LongInt);
    begin
      FField2 := AValue;
    end;
    
    { TTestIntf3 }
    
    function TTestIntf3.GetField1: LongInt;
    begin
      Result := FField1;
    end;
    
    function TTestIntf3.GetField2: Double;
    begin
      Result := FField2;
    end;
    
    function TTestIntf3.GetField3: string;
    begin
      Result := FField3;
    end;
    
    function TTestIntf3.GetField4: Extended;
    begin
      Result := FField4;
    end;
    
    procedure TTestIntf3.SetField1(AValue: LongInt);
    begin
      FField1 := AValue;
    end;
    
    procedure TTestIntf3.SetField2(AValue: Double);
    begin
      FField2 := AValue;
    end;
    
    procedure TTestIntf3.SetField3(const AValue: string);
    begin
      FField3 := AValue;
    end;
    
    procedure TTestIntf3.SetField4(AValue: Extended);
    begin
      FField4 := AValue;
    end;
    
    { B8 }
    
    function TestB8(P1, P2: Boolean): Boolean;
    begin
      Assert((not P1) and P2);
      Result := False;
    end;
    
    function TestManyB8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Boolean): Boolean;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := True;
    end;
    
    { B16 }
    
    function TestB16(P1, P2: Boolean16): Boolean16;
    begin
      Assert((not P1) and P2);
      Result := Boolean16(0);
    end;
    
    function TestManyB16(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: Boolean16): Boolean16;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := Boolean16(1);
    end;
    
    { B32 }
    
    function TestB32(P1, P2: Boolean32): Boolean32;
    begin
      Assert((not P1) and P2);
      Result := Boolean32(0);
    end;
    
    function TestManyB32(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: Boolean32): Boolean32;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := Boolean32(1);
    end;
    
    { B64 }
    
    function TestB64(P1, P2: Boolean64): Boolean64;
    begin
      Assert((not P1) and P2);
      Result := Boolean64(0);
    end;
    
    function TestManyB64(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: Boolean64): Boolean64;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := Boolean64(1);
    end;
    
    { BL8 }
    
    function TestBL8(P1, P2: ByteBool): ByteBool;
    begin
      Assert((not P1) and P2);
      Result := ByteBool(0);
    end;
    
    function TestManyBL8(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: ByteBool): ByteBool;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := ByteBool(1);
    end;
    
    { BL16 }
    
    function TestBL16(P1, P2: WordBool): WordBool;
    begin
      Assert((not P1) and P2);
      Result := WordBool(0);
    end;
    
    function TestManyBL16(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: WordBool): WordBool;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := WordBool(1);
    end;
    
    { BL32 }
    
    function TestBL32(P1, P2: LongBool): LongBool;
    begin
      Assert((not P1) and P2);
      Result := LongBool(0);
    end;
    
    function TestManyBL32(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: LongBool): LongBool;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := LongBool(1);
    end;
    
    { BL64 }
    
    function TestBL64(P1, P2: QWordBool): QWordBool;
    begin
      Assert((not P1) and P2);
      Result := QWordBool(0);
    end;
    
    function TestManyBL64(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: QWordBool): QWordBool;
    begin
      Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
        (not P7) and P8 and (not P9) and P10);
      Result := QWordBool(1);
    end;
    
    { U8 }
    
    function TestU8(P1, P2: UInt8): UInt8;
    begin
      Assert((P1 = Low(UInt8)) and (P2 = High(UInt8)));
      Result := High(UInt8);
    end;
    
    function TestManyU8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt8): UInt8;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(UInt8)) and (P10 = High(UInt8)));
      Result := High(UInt8);
    end;
    
    { S8 }
    
    function TestS8(P1, P2: Int8): Int8;
    begin
      Assert((P1 = Low(Int8)) and (P2 = High(Int8)));
      Result := High(Int8);
    end;
    
    function TestManyS8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int8): Int8;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(Int8)) and (P10 = High(Int8)));
      Result := High(Int8);
    end;
    
    { U16 }
    
    function TestU16(P1, P2: UInt16): UInt16;
    begin
      Assert((P1 = Low(UInt16)) and (P2 = High(UInt16)));
      Result := High(UInt16);
    end;
    
    function TestManyU16(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt16): UInt16;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(UInt16)) and (P10 = High(UInt16)));
      Result := High(UInt16);
    end;
    
    { S16 }
    
    function TestS16(P1, P2: Int16): Int16;
    begin
      Assert((P1 = Low(Int16)) and (P2 = High(Int16)));
      Result := High(Int16);
    end;
    
    function TestManyS16(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int16): Int16;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(Int16)) and (P10 = High(Int16)));
      Result := High(Int16);
    end;
    
    { U32 }
    
    function TestU32(P1, P2: UInt32): UInt32;
    begin
      Assert((P1 = Low(UInt32)) and (P2 = High(UInt32)));
      Result := High(UInt32);
    end;
    
    function TestManyU32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt32): UInt32;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(UInt32)) and (P10 = High(UInt32)));
      Result := High(UInt32);
    end;
    
    { S32 }
    
    function TestS32(P1, P2: Int32): Int32;
    begin
      Assert((P1 = Low(Int32)) and (P2 = High(Int32)));
      Result := High(Int32);
    end;
    
    function TestManyS32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int32): Int32;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(Int32)) and (P10 = High(Int32)));
      Result := High(Int32);
    end;
    
    { U64 }
    
    function TestU64(P1, P2: UInt64): UInt64;
    begin
      Assert((P1 = Low(UInt64)) and (P2 = High(UInt64)));
      Result := High(UInt64);
    end;
    
    function TestManyU64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt64): UInt64;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(UInt64)) and (P10 = High(UInt64)));
      Result := High(UInt64);
    end;
    
    { S64 }
    
    function TestS64(P1, P2: Int64): Int64;
    begin
      Assert((P1 = Low(Int64)) and (P2 = High(Int64)));
      Result := High(Int64);
    end;
    
    function TestManyS64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int64): Int64;
    begin
      Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
        (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
        (P9 = Low(Int64)) and (P10 = High(Int64)));
      Result := High(Int64);
    end;
    
    { F32 }
    
    function TestF32(P1, P2: Single): Single;
    begin
      Assert(EqF32(P1, MinSingle) and EqF32(P2, MaxSingle));
      Result := MaxSingle;
    end;
    
    function TestManyF32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Single): Single;
    begin
      Assert(EqF32(P1, 11.11) and EqF32(P2, 22.22) and EqF32(P3, 33.33) and
        EqF32(P4, 44.44) and EqF32(P5, 55.55) and EqF32(P6, 66.66) and
        EqF32(P7, 77.77) and EqF32(P8, 88.88) and EqF32(P9, MinSingle) and
        EqF32(P10, MaxSingle));
      Result := MaxSingle;
    end;
    
    { F64 }
    
    function TestF64(P1, P2: Double): Double;
    begin
      Assert(EqF64(P1, MinDouble) and EqF64(P2, MaxDouble));
      Result := MaxDouble;
    end;
    
    function TestManyF64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Double): Double;
    begin
      Assert(EqF64(P1, 11.11) and EqF64(P2, 22.22) and EqF64(P3, 33.33) and
        EqF64(P4, 44.44) and EqF64(P5, 55.55) and EqF64(P6, 66.66) and
        EqF64(P7, 77.77) and EqF64(P8, 88.88) and EqF64(P9, MinDouble) and
        EqF64(P10, MaxDouble));
      Result := MaxDouble;
    end;
    
    { FCU }
    
    function TestFCU(P1, P2: Currency): Currency;
    begin
      Assert(EqF64(P1, MinCurrency) and EqF64(P2, MaxCurrency));
      Result := MaxCurrency;
    end;
    
    function TestManyFCU(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: Currency): Currency;
    begin
      Assert(EqF64(P1, 11.11) and EqF64(P2, 22.22) and EqF64(P3, 33.33) and
        EqF64(P4, 44.44) and EqF64(P5, 55.55) and EqF64(P6, 66.66) and
        EqF64(P7, 77.77) and EqF64(P8, 88.88) and EqF64(P9, MinCurrency) and
        EqF64(P10, MaxCurrency));
      Result := MaxCurrency;
    end;
    
    { FCO }
    
    function TestFCO(P1, P2: Comp): Comp;
    begin
      Assert((P1 = 123) and (P2 = 456));
      Result := 789;
    end;
    
    { F80 }
    
    function TestF80(P1, P2: Extended): Extended;
    begin
      Assert(EqF80(P1, MinExtended) and EqF80(P2, MaxExtended));
      Result := MaxExtended;
    end;
    
    function TestManyF80(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: Extended): Extended;
    begin
      Assert(EqF80(P1, 11.11) and EqF80(P2, 22.22) and EqF80(P3, 33.33) and
        EqF80(P4, 44.44) and EqF80(P5, 55.55) and EqF80(P6, 66.66) and
        EqF80(P7, 77.77) and EqF80(P8, 88.88) and EqF80(P9, MinExtended) and
        EqF80(P10, MaxExtended));
      Result := MaxExtended;
    end;
    
    { SS }
    
    function TestSS(P1, P2: ShortString): ShortString;
    begin
      Assert((P1 = 'abc') and (P2 = '123'));
      Result := 'abc123';
    end;
    
    function TestManySS(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: ShortString): ShortString;
    begin
      Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
        (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
        (P9 = 'S8') and (P10 = 'S9'));
      Result := 'abc123';
    end;
    
    { SA }
    
    function TestSA(P1, P2: AnsiString): AnsiString;
    begin
      Assert((P1 = 'abc') and (P2 = '123'));
      Result := 'abc123';
    end;
    
    function TestManySA(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: AnsiString): AnsiString;
    begin
      Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
        (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
        (P9 = 'S8') and (P10 = 'S9'));
      Result := 'abc123';
    end;
    
    { SU }
    
    function TestSU(P1, P2: UnicodeString): UnicodeString;
    begin
      Assert((P1 = 'abc') and (P2 = '123'));
      Result := 'abc123';
    end;
    
    function TestManySU(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: UnicodeString): UnicodeString;
    begin
      Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
        (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
        (P9 = 'S8') and (P10 = 'S9'));
      Result := 'abc123';
    end;
    
    { SW }
    
    function TestSW(P1, P2: WideString): WideString;
    begin
      Assert((P1 = 'abc') and (P2 = '123'));
      Result := 'abc123';
    end;
    
    function TestManySW(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: WideString): WideString;
    begin
      Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
        (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
        (P9 = 'S8') and (P10 = 'S9'));
      Result := 'abc123';
    end;
    
    { P }
    
    function TestP(P1, P2: Pointer): Pointer;
    begin
      Assert((P1 = @GVal1) and (P2 = @GVal2));
      Result := @GVal3;
    end;
    
    function TestManyP(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Pointer): Pointer;
    begin
      Assert((P1 = @GVal1) and (P2 = @GVal2) and (P3 = @GVal3) and (P4 = @GVal1) and
        (P5 = @GVal2) and (P6 = @GVal3) and (P7 = @GVal1) and (P8 = @GVal2) and
        (P9 = @GVal3) and (P10 = @GVal1));
      Result := @GVal3;
    end;
    
    { AD }
    
    function TestAD(P1, P2: TArrayOfLongIntDyn): TArrayOfLongIntDyn;
    begin
      Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]));
      Result := [147, 258, 369];
    end;
    
    function TestManyAD(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TArrayOfLongIntDyn): TArrayOfLongIntDyn;
    begin
      Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]) and
        EqArr(P3, [123, 456]) and EqArr(P4, [789, 654, 321]) and
        EqArr(P5, [123, 456]) and EqArr(P6, [789, 654, 321]) and
        EqArr(P7, [123, 456]) and EqArr(P8, [789, 654, 321]) and
        EqArr(P9, [123, 456]) and EqArr(P10, [789, 654, 321]));
      Result := [963, 852, 741];
    end;
    
    { AS }
    
    function TestAS(P1, P2: TArrayOfLongIntStatic): TArrayOfLongIntStatic;
    begin
      Assert(EqArr(P1, [123, 456, 0]) and EqArr(P2, [789, 654, 321]));
      Result[0] := 147;
      Result[1] := 258;
      Result[2] := 369;
    end;
    
    function TestManyAS(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TArrayOfLongIntStatic): TArrayOfLongIntStatic;
    begin
      Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]) and
        EqArr(P3, [123, 456]) and EqArr(P4, [789, 654, 321]) and
        EqArr(P5, [123, 456]) and EqArr(P6, [789, 654, 321]) and
        EqArr(P7, [123, 456]) and EqArr(P8, [789, 654, 321]) and
        EqArr(P9, [123, 456]) and EqArr(P10, [789, 654, 321]));
      Result[0] := 963;
      Result[1] := 852;
      Result[2] := 741;
    end;
    
    { E }
    
    function TestE(P1, P2: TTestEnum): TTestEnum;
    begin
      Assert((P1 = Low(TTestEnum)) and (P2 = High(TTestEnum)));
      Result := High(TTestEnum);
    end;
    
    function TestManyE(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestEnum): TTestEnum;
    begin
      Assert((P1 = te1) and (P2 = te10) and (P3 = te1) and (P4 = te10) and
        (P5 = te1) and (P6 = te10) and (P7 = te1) and (P8 = te10) and
        (P9 = te1) and (P10 = te10));
      Result := High(TTestEnum);
    end;
    
    { S }
    
    function TestS(P1, P2: TTestSet): TTestSet;
    begin
      Assert((te1 in P1) and (te10 in P2));
      Result := [te1, te2, te3];
    end;
    
    function TestManyS(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: TTestSet): TTestSet;
    begin
      Assert((P1 = [te1]) and (P2 = [te10]) and (P3 = [te1]) and (P4 = [te10]) and
        (P5 = [te1]) and (P6 = [te10]) and (P7 = [te1]) and (P8 = [te10]) and
        (P9 = [te1]) and (P10 = [te10]));
      Result := [te8, te9, te10];
    end;
    
    { Proc }
    
    function TestProc(P1, P2: TTestProc): TTestProc;
    begin
      Assert((@P1 = @TestProcedure1) and (@P2 = @TestProcedure2));
      P1;
      P2;
      Result := TestProcedure1;
    end;
    
    function TestManyProc(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestProc): TTestProc;
    begin
      Assert((@P1 = @TestProcedure1) and (@P2 = @TestProcedure2) and
        (@P3 = @TestProcedure1) and (@P4 = @TestProcedure2) and
        (@P5 = @TestProcedure1) and (@P6 = @TestProcedure2) and
        (@P7 = @TestProcedure1) and (@P8 = @TestProcedure2) and
        (@P9 = @TestProcedure1) and (@P10 = @TestProcedure2));
      P1;
      P10;
      Result := TestProcedure1;
    end;
    
    { Func 1 }
    
    function TestFunc1(P1, P2: TTestFunc1): TTestFunc1;
    begin
      Assert((@P1 = @TestFunction1) and (@P2 = @TestFunction2));
      Assert(P1 = 123);
      Assert(P2 = 456);
      Result := TestFunction1;
    end;
    
    function TestManyFunc1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestFunc1): TTestFunc1;
    begin
      Assert((@P1 = @TestFunction1) and (@P2 = @TestFunction2) and
        (@P3 = @TestFunction1) and (@P4 = @TestFunction2) and
        (@P5 = @TestFunction1) and (@P6 = @TestFunction2) and
        (@P7 = @TestFunction1) and (@P8 = @TestFunction2) and
        (@P9 = @TestFunction1) and (@P10 = @TestFunction2));
      Assert(P1 = 123);
      Assert(P2 = 456);
      Assert(P3 = 123);
      Assert(P4 = 456);
      Assert(P5 = 123);
      Assert(P6 = 456);
      Assert(P7 = 123);
      Assert(P8 = 456);
      Assert(P9 = 123);
      Assert(P10 = 456);
      Result := TestFunction2;
    end;
    
    { Func 2 }
    
    function TestFunc2(P1, P2: TTestFunc2): TTestFunc2;
    begin
      Assert((@P1 = @TestFunction3) and (@P2 = @TestFunction4));
      Assert(P1(123, [456, 789]) = '123456789');
      Assert(P2(456, [789, 456, 123]) = '456789456123abc123');
      Result := TestFunction3;
    end;
    
    function TestManyFunc2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestFunc2): TTestFunc2;
    begin
      Assert((@P1 = @TestFunction3) and (@P2 = @TestFunction4) and
        (@P3 = @TestFunction3) and (@P4 = @TestFunction4) and
        (@P5 = @TestFunction3) and (@P6 = @TestFunction4) and
        (@P7 = @TestFunction3) and (@P8 = @TestFunction4) and
        (@P9 = @TestFunction3) and (@P10 = @TestFunction4));
      Assert(P1(123, [456, 789]) = '123456789');
      Assert(P10(456, [789, 456, 123]) = '456789456123abc123');
      Result := TestFunction4;
    end;
    
    { Method }
    
    function TestMethod1(S: TTestMethodObj; P1, P2: TTestMethod1): TTestMethod1;
    begin
      Assert((@P1 = @TTestMethodObj.TestMethod1) and
        (@P2 = @TTestMethodObj.TestMethod2));
      P1;
      P2;
      Result := S.TestMethod1;
    end;
    
    function TestManyMethod1(S: TTestMethodObj; P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestMethod1): TTestMethod1;
    begin
      Assert((@P1 = @TTestMethodObj.TestMethod1) and
        (@P2 = @TTestMethodObj.TestMethod2) and
        (@P3 = @TTestMethodObj.TestMethod1) and
        (@P4 = @TTestMethodObj.TestMethod2) and
        (@P5 = @TTestMethodObj.TestMethod1) and
        (@P6 = @TTestMethodObj.TestMethod2) and
        (@P7 = @TTestMethodObj.TestMethod1) and
        (@P8 = @TTestMethodObj.TestMethod2) and
        (@P9 = @TTestMethodObj.TestMethod1) and
        (@P10 = @TTestMethodObj.TestMethod2));
      P1;
      P10;
      Result := S.TestMethod1;
    end;
    
    { R1 }
    
    function TestRecord1(P1, P2: TTestRecord1): TTestRecord1;
    begin
      Assert((P1.Member1 = 123) and (P1.Member2 = 456) and (P2.Member1 = 987) and
        (P2.Member2 = 654));
      Result.Member1 := 147;
      Result.Member2 := 258;
    end;
    
    function TestManyRecord1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestRecord1): TTestRecord1;
    begin
      Assert((P1.Member1 = 123) and (P1.Member2 = 456) and (P2.Member1 = 987) and
        (P2.Member2 = 654) and (P3.Member1 = 123) and (P3.Member2 = 456) and
        (P4.Member1 = 987) and (P4.Member2 = 654) and (P5.Member1 = 123) and
        (P5.Member2 = 456) and (P6.Member1 = 987) and (P6.Member2 = 654) and
        (P7.Member1 = 123) and (P7.Member2 = 456) and (P8.Member1 = 987) and
        (P8.Member2 = 654) and (P9.Member1 = 123) and (P9.Member2 = 456) and
        (P10.Member1 = 987) and (P10.Member2 = 654));
      Result.Member1 := 258;
      Result.Member2 := 147;
    end;
    
    { R2 }
    
    function TestRecord2(P1, P2: TTestRecord2): TTestRecord2;
    begin
      Assert((P1.Member1 = 'abc') and (P1.Member2 = 123) and
        (P2.Member1 = 'def') and (P2.Member2 = 456));
      Result.Member1 := 'ghi';
      Result.Member2 := 789;
    end;
    
    function TestManyRecord2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestRecord2): TTestRecord2;
    begin
      Assert((P1.Member1 = 'abc') and (P1.Member2 = 123) and
        (P2.Member1 = 'def') and (P2.Member2 = 456) and (P3.Member1 = 'abc') and
        (P3.Member2 = 123) and (P4.Member1 = 'def') and (P4.Member2 = 456) and
        (P5.Member1 = 'abc') and (P5.Member2 = 123) and (P6.Member1 = 'def') and
        (P6.Member2 = 456) and (P7.Member1 = 'abc') and (P7.Member2 = 123) and
        (P8.Member1 = 'def') and (P8.Member2 = 456) and (P9.Member1 = 'abc') and
        (P9.Member2 = 123) and (P10.Member1 = 'def') and (P10.Member2 = 456));
      Result.Member1 := 'xyz';
      Result.Member2 := 951;
    end;
    
    { R3 }
    
    function TestRecord3(P1, P2: TTestRecord3): TTestRecord3;
    begin
      Assert((P1.Member1 = 11) and EqF64(P1.Member2, 22.22) and
        (P1.Member3 = 'aaa') and EqF80(P1.Member4, 33.33) and
        (P2.Member1 = 22) and EqF64(P2.Member2, 44.44) and
        (P2.Member3 = 'bbb') and EqF80(P2.Member4, 55.55));
      Result.Member1 := 123;
      Result.Member2 := 12.34;
      Result.Member3 := 'abc';
      Result.Member4 := 45.67;
    end;
    
    function TestManyRecord3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestRecord3): TTestRecord3;
    begin
      Assert((P1.Member1 = 11) and EqF64(P1.Member2, 22.22) and
        (P1.Member3 = 'aaa') and EqF80(P1.Member4, 33.33) and
        (P2.Member1 = 22) and EqF64(P2.Member2, 44.44) and
        (P2.Member3 = 'bbb') and EqF80(P2.Member4, 55.55) and
        (P3.Member1 = 11) and EqF64(P3.Member2, 22.22) and
        (P3.Member3 = 'aaa') and EqF80(P3.Member4, 33.33) and
        (P4.Member1 = 22) and EqF64(P4.Member2, 44.44) and
        (P4.Member3 = 'bbb') and EqF80(P4.Member4, 55.55) and
        (P5.Member1 = 11) and EqF64(P5.Member2, 22.22) and
        (P5.Member3 = 'aaa') and EqF80(P5.Member4, 33.33) and
        (P6.Member1 = 22) and EqF64(P6.Member2, 44.44) and
        (P6.Member3 = 'bbb') and EqF80(P6.Member4, 55.55) and
        (P7.Member1 = 11) and EqF64(P7.Member2, 22.22) and
        (P7.Member3 = 'aaa') and EqF80(P7.Member4, 33.33) and
        (P8.Member1 = 22) and EqF64(P8.Member2, 44.44) and
        (P8.Member3 = 'bbb') and EqF80(P8.Member4, 55.55) and
        (P9.Member1 = 11) and EqF64(P9.Member2, 22.22) and
        (P9.Member3 = 'aaa') and EqF80(P9.Member4, 33.33) and
        (P10.Member1 = 22) and EqF64(P10.Member2, 44.44) and
        (P10.Member3 = 'bbb') and EqF80(P10.Member4, 55.55));
      Result.Member1 := 321;
      Result.Member2 := 43.21;
      Result.Member3 := 'def';
      Result.Member4 := 76.54;
    end;
    
    { C1 }
    
    function TestClass1(P1, P2: TTestClass1): TTestClass1;
    begin
      Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
        (P2.Field2 = 654));
      Result := TTestClass1.Create;
      Result.Field1 := 147;
      Result.Field2 := 258;
    end;
    
    function TestManyClass1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestClass1): TTestClass1;
    begin
      Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
        (P2.Field2 = 654) and (P3.Field1 = 123) and (P3.Field2 = 456) and
        (P4.Field1 = 987) and (P4.Field2 = 654) and (P5.Field1 = 123) and
        (P5.Field2 = 456) and (P6.Field1 = 987) and (P6.Field2 = 654) and
        (P7.Field1 = 123) and (P7.Field2 = 456) and (P8.Field1 = 987) and
        (P8.Field2 = 654) and (P9.Field1 = 123) and (P9.Field2 = 456) and
        (P10.Field1 = 987) and (P10.Field2 = 654));
      Result := TTestClass1.Create;
      Result.Field1 := 258;
      Result.Field2 := 147;
    end;
    
    { C2 }
    
    function TestClass2(P1, P2: TTestClass2): TTestClass2;
    begin
      Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
        (P2.Field1 = 'def') and (P2.Field2 = 456));
      Result := TTestClass2.Create;
      Result.Field1 := 'ghi';
      Result.Field2 := 789;
    end;
    
    function TestManyClass2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestClass2): TTestClass2;
    begin
      Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
        (P2.Field1 = 'def') and (P2.Field2 = 456) and (P3.Field1 = 'abc') and
        (P3.Field2 = 123) and (P4.Field1 = 'def') and (P4.Field2 = 456) and
        (P5.Field1 = 'abc') and (P5.Field2 = 123) and (P6.Field1 = 'def') and
        (P6.Field2 = 456) and (P7.Field1 = 'abc') and (P7.Field2 = 123) and
        (P8.Field1 = 'def') and (P8.Field2 = 456) and (P9.Field1 = 'abc') and
        (P9.Field2 = 123) and (P10.Field1 = 'def') and (P10.Field2 = 456));
      Result := TTestClass2.Create;
      Result.Field1 := 'xyz';
      Result.Field2 := 951;
    end;
    
    { C3 }
    
    function TestClass3(P1, P2: TTestClass3): TTestClass3;
    begin
      Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
        (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
        (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
        (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55));
      Result := TTestClass3.Create;
      Result.Field1 := 123;
      Result.Field2 := 12.34;
      Result.Field3 := 'abc';
      Result.Field4 := 45.67;
    end;
    
    function TestManyClass3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: TTestClass3): TTestClass3;
    begin
      Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
        (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
        (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
        (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55) and
        (P3.Field1 = 11) and EqF64(P3.Field2, 22.22) and
        (P3.Field3 = 'aaa') and EqF80(P3.Field4, 33.33) and
        (P4.Field1 = 22) and EqF64(P4.Field2, 44.44) and
        (P4.Field3 = 'bbb') and EqF80(P4.Field4, 55.55) and
        (P5.Field1 = 11) and EqF64(P5.Field2, 22.22) and
        (P5.Field3 = 'aaa') and EqF80(P5.Field4, 33.33) and
        (P6.Field1 = 22) and EqF64(P6.Field2, 44.44) and
        (P6.Field3 = 'bbb') and EqF80(P6.Field4, 55.55) and
        (P7.Field1 = 11) and EqF64(P7.Field2, 22.22) and
        (P7.Field3 = 'aaa') and EqF80(P7.Field4, 33.33) and
        (P8.Field1 = 22) and EqF64(P8.Field2, 44.44) and
        (P8.Field3 = 'bbb') and EqF80(P8.Field4, 55.55) and
        (P9.Field1 = 11) and EqF64(P9.Field2, 22.22) and
        (P9.Field3 = 'aaa') and EqF80(P9.Field4, 33.33) and
        (P10.Field1 = 22) and EqF64(P10.Field2, 44.44) and
        (P10.Field3 = 'bbb') and EqF80(P10.Field4, 55.55));
      Result := TTestClass3.Create;
      Result.Field1 := 321;
      Result.Field2 := 43.21;
      Result.Field3 := 'def';
      Result.Field4 := 76.54;
    end;
    
    { I1 }
    
    function TestIntf1(P1, P2: ITestIntf1): ITestIntf1;
    begin
      Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
        (P2.Field2 = 654));
      Result := TTestIntf1.Create;
      Result.Field1 := 147;
      Result.Field2 := 258;
    end;
    
    function TestManyIntf1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: ITestIntf1): ITestIntf1;
    begin
      Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
        (P2.Field2 = 654) and (P3.Field1 = 123) and (P3.Field2 = 456) and
        (P4.Field1 = 987) and (P4.Field2 = 654) and (P5.Field1 = 123) and
        (P5.Field2 = 456) and (P6.Field1 = 987) and (P6.Field2 = 654) and
        (P7.Field1 = 123) and (P7.Field2 = 456) and (P8.Field1 = 987) and
        (P8.Field2 = 654) and (P9.Field1 = 123) and (P9.Field2 = 456) and
        (P10.Field1 = 987) and (P10.Field2 = 654));
      Result := TTestIntf1.Create;
      Result.Field1 := 258;
      Result.Field2 := 147;
    end;
    
    { I2 }
    
    function TestIntf2(P1, P2: ITestIntf2): ITestIntf2;
    begin
      Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
        (P2.Field1 = 'def') and (P2.Field2 = 456));
      Result := TTestIntf2.Create;
      Result.Field1 := 'ghi';
      Result.Field2 := 789;
    end;
    
    function TestManyIntf2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: ITestIntf2): ITestIntf2;
    begin
      Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
        (P2.Field1 = 'def') and (P2.Field2 = 456) and (P3.Field1 = 'abc') and
        (P3.Field2 = 123) and (P4.Field1 = 'def') and (P4.Field2 = 456) and
        (P5.Field1 = 'abc') and (P5.Field2 = 123) and (P6.Field1 = 'def') and
        (P6.Field2 = 456) and (P7.Field1 = 'abc') and (P7.Field2 = 123) and
        (P8.Field1 = 'def') and (P8.Field2 = 456) and (P9.Field1 = 'abc') and
        (P9.Field2 = 123) and (P10.Field1 = 'def') and (P10.Field2 = 456));
      Result := TTestIntf2.Create;
      Result.Field1 := 'xyz';
      Result.Field2 := 951;
    end;
    
    { I3 }
    
    function TestIntf3(P1, P2: ITestIntf3): ITestIntf3;
    begin
      Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
        (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
        (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
        (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55));
      Result := TTestIntf3.Create;
      Result.Field1 := 123;
      Result.Field2 := 12.34;
      Result.Field3 := 'abc';
      Result.Field4 := 45.67;
    end;
    
    function TestManyIntf3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
      P10: ITestIntf3): ITestIntf3;
    begin
      Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
        (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
        (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
        (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55) and
        (P3.Field1 = 11) and EqF64(P3.Field2, 22.22) and
        (P3.Field3 = 'aaa') and EqF80(P3.Field4, 33.33) and
        (P4.Field1 = 22) and EqF64(P4.Field2, 44.44) and
        (P4.Field3 = 'bbb') and EqF80(P4.Field4, 55.55) and
        (P5.Field1 = 11) and EqF64(P5.Field2, 22.22) and
        (P5.Field3 = 'aaa') and EqF80(P5.Field4, 33.33) and
        (P6.Field1 = 22) and EqF64(P6.Field2, 44.44) and
        (P6.Field3 = 'bbb') and EqF80(P6.Field4, 55.55) and
        (P7.Field1 = 11) and EqF64(P7.Field2, 22.22) and
        (P7.Field3 = 'aaa') and EqF80(P7.Field4, 33.33) and
        (P8.Field1 = 22) and EqF64(P8.Field2, 44.44) and
        (P8.Field3 = 'bbb') and EqF80(P8.Field4, 55.55) and
        (P9.Field1 = 11) and EqF64(P9.Field2, 22.22) and
        (P9.Field3 = 'aaa') and EqF80(P9.Field4, 33.33) and
        (P10.Field1 = 22) and EqF64(P10.Field2, 44.44) and
        (P10.Field3 = 'bbb') and EqF80(P10.Field4, 55.55));
      Result := TTestIntf3.Create;
      Result.Field1 := 321;
      Result.Field2 := 43.21;
      Result.Field3 := 'def';
      Result.Field4 := 76.54;
    end;
    
    var
      I: Integer;
      V1, V2: TValue;
      VA: array of TValue = nil;
      B8: Boolean;
      B16: Boolean16;
      B32: Boolean32;
      B64: Boolean64;
      BL8: ByteBool;
      BL16: WordBool;
      BL32: LongBool;
      BL64: QWordBool;
      U8: UInt8;
      S8: Int8;
      U16: UInt16;
      S16: Int16;
      U32: UInt32;
      S32: Int32;
      U64: UInt64;
      S64: Int64;
      F32: Single;
      F64: Double;
      FCU: Currency;
      FCO: Comp;
      F80: Extended;
      SS: ShortString;
      SA: AnsiString;
      SU: UnicodeString;
      SW: WideString;
      P: Pointer;
      AD: TArrayOfLongIntDyn;
      AS_: TArrayOfLongIntStatic;
      E: TTestEnum;
      S: TTestSet;
      Proc: TTestProc;
      Func1: TTestFunc1;
      Func2: TTestFunc2;
      Method1: TTestMethod1;
      TestMethodObj: TTestMethodObj;
      R1: TTestRecord1;
      R2: TTestRecord2;
      R3: TTestRecord3;
      C1, C12, C1R: TTestClass1;
      C2, C22, C2R: TTestClass2;
      C3, C32, C3R: TTestClass3;
      I1, I12, I1R: ITestIntf1;
      I2, I22, I2R: ITestIntf2;
      I3, I32, I3R: ITestIntf3;
    begin
      InitSystemFunctionCallManager;
    
      Rtti.Invoke(@TestProcedure1, nil, ccReg, nil, True, False);
      Assert(GProcOK1);
    
      { B8 }
    
      B8 := False;
      TValue.Make(@B8, TypeInfo(Boolean), V1);
      B8 := True;
      TValue.Make(@B8, TypeInfo(Boolean), V2);
      Assert(not Rtti.Invoke(@TestB8, [V1, V2], ccReg, TypeInfo(Boolean),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyB8, [Boolean(False), Boolean(True),
        Boolean(False), Boolean(True), Boolean(False), Boolean(True),
        Boolean(False), Boolean(True), Boolean(False), Boolean(True)],
        ccReg, TypeInfo(Boolean), True, False).AsBoolean);
    
      { B16 }
    
      B16 := False;
      TValue.Make(@B16, TypeInfo(Boolean16), V1);
      B16 := True;
      TValue.Make(@B16, TypeInfo(Boolean16), V2);
      Assert(not Rtti.Invoke(@TestB16, [V1, V2], ccReg, TypeInfo(Boolean16),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyB16, [Boolean16(False), Boolean16(True),
        Boolean16(False), Boolean16(True), Boolean16(False), Boolean16(True),
        Boolean16(False), Boolean16(True), Boolean16(False), Boolean16(True)],
        ccReg, TypeInfo(Boolean16), True, False).AsBoolean);
    
      { B32 }
    
      B32 := False;
      TValue.Make(@B32, TypeInfo(Boolean32), V1);
      B32 := True;
      TValue.Make(@B32, TypeInfo(Boolean32), V2);
      Assert(not Rtti.Invoke(@TestB32, [V1, V2], ccReg, TypeInfo(Boolean32),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyB32, [Boolean32(False), Boolean32(True),
        Boolean32(False), Boolean32(True), Boolean32(False), Boolean32(True),
        Boolean32(False), Boolean32(True), Boolean32(False), Boolean32(True)],
        ccReg, TypeInfo(Boolean32), True, False).AsBoolean);
    
      { B64 }
    
      B64 := False;
      TValue.Make(@B64, TypeInfo(Boolean64), V1);
      B64 := True;
      TValue.Make(@B64, TypeInfo(Boolean64), V2);
      Assert(not Rtti.Invoke(@TestB64, [V1, V2], ccReg, TypeInfo(Boolean64),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyB64, [Boolean64(False), Boolean64(True),
        Boolean64(False), Boolean64(True), Boolean64(False), Boolean64(True),
        Boolean64(False), Boolean64(True), Boolean64(False), Boolean64(True)],
        ccReg, TypeInfo(Boolean64), True, False).AsBoolean);
    
      { BL8 }
    
      BL8 := False;
      TValue.Make(@BL8, TypeInfo(ByteBool), V1);
      BL8 := True;
      TValue.Make(@BL8, TypeInfo(ByteBool), V2);
      Assert(not Rtti.Invoke(@TestBL8, [V1, V2], ccReg, TypeInfo(ByteBool),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyBL8, [ByteBool(False), ByteBool(True),
        ByteBool(False), ByteBool(True), ByteBool(False), ByteBool(True),
        ByteBool(False), ByteBool(True), ByteBool(False), ByteBool(True)],
        ccReg, TypeInfo(ByteBool), True, False).AsBoolean);
    
      { BL16 }
    
      BL16 := False;
      TValue.Make(@BL16, TypeInfo(WordBool), V1);
      BL16 := True;
      TValue.Make(@BL16, TypeInfo(WordBool), V2);
      Assert(not Rtti.Invoke(@TestBL16, [V1, V2], ccReg, TypeInfo(WordBool),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyBL16, [WordBool(False), WordBool(True),
        WordBool(False), WordBool(True), WordBool(False), WordBool(True),
        WordBool(False), WordBool(True), WordBool(False), WordBool(True)],
        ccReg, TypeInfo(WordBool), True, False).AsBoolean);
    
      { BL32 }
    
      BL32 := False;
      TValue.Make(@BL32, TypeInfo(LongBool), V1);
      BL32 := True;
      TValue.Make(@BL32, TypeInfo(LongBool), V2);
      Assert(not Rtti.Invoke(@TestBL32, [V1, V2], ccReg, TypeInfo(LongBool),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyBL32, [LongBool(False), LongBool(True),
        LongBool(False), LongBool(True), LongBool(False), LongBool(True),
        LongBool(False), LongBool(True), LongBool(False), LongBool(True)],
        ccReg, TypeInfo(LongBool), True, False).AsBoolean);
    
      { BL64 }
    
      BL64 := False;
      TValue.Make(@BL64, TypeInfo(QWordBool), V1);
      BL64 := True;
      TValue.Make(@BL64, TypeInfo(QWordBool), V2);
      Assert(not Rtti.Invoke(@TestBL64, [V1, V2], ccReg, TypeInfo(QWordBool),
        True, False).AsBoolean);
      Assert(Rtti.Invoke(@TestManyBL64, [QWordBool(False), QWordBool(True),
        QWordBool(False), QWordBool(True), QWordBool(False), QWordBool(True),
        QWordBool(False), QWordBool(True), QWordBool(False), QWordBool(True)],
        ccReg, TypeInfo(QWordBool), True, False).AsBoolean);
    
      { U8 }
    
      U8 := Low(UInt8);
      TValue.Make(@U8, TypeInfo(UInt8), V1);
      U8 := High(UInt8);
      TValue.Make(@U8, TypeInfo(UInt8), V2);
      Assert(Rtti.Invoke(@TestU8, [V1, V2], ccReg, TypeInfo(UInt8),
        True, False).AsInteger = High(UInt8));
      Assert(Rtti.Invoke(@TestManyU8, [UInt8(11), UInt8(22), UInt8(33), UInt8(44),
        UInt8(55), UInt8(66), UInt8(77), UInt8(88), UInt8(Low(UInt8)),
        UInt8(High(UInt8))], ccReg, TypeInfo(UInt8),
        True, False).AsInteger = High(UInt8));
    
      { S8 }
    
      S8 := Low(Int8);
      TValue.Make(@S8, TypeInfo(Int8), V1);
      S8 := High(Int8);
      TValue.Make(@S8, TypeInfo(Int8), V2);
      Assert(Rtti.Invoke(@TestS8, [V1, V2], ccReg, TypeInfo(Int8),
        True, False).AsInteger = High(Int8));
      Assert(Rtti.Invoke(@TestManyS8, [Int8(11), Int8(22), Int8(33), Int8(44),
        Int8(55), Int8(66), Int8(77), Int8(88), Int8(Low(Int8)),
        Int8(High(Int8))], ccReg, TypeInfo(Int8),
        True, False).AsInteger = High(Int8));
    
      { U16 }
    
      U16 := Low(UInt16);
      TValue.Make(@U16, TypeInfo(UInt16), V1);
      U16 := High(UInt16);
      TValue.Make(@U16, TypeInfo(UInt16), V2);
      Assert(Rtti.Invoke(@TestU16, [V1, V2], ccReg, TypeInfo(UInt16),
        True, False).AsInteger = High(UInt16));
      Assert(Rtti.Invoke(@TestManyU16, [UInt16(11), UInt16(22), UInt16(33),
        UInt16(44), UInt16(55), UInt16(66), UInt16(77), UInt16(88),
        UInt16(Low(UInt16)), UInt16(High(UInt16))], ccReg, TypeInfo(UInt16),
        True, False).AsInteger = High(UInt16));
    
      { S16 }
    
      S16 := Low(Int16);
      TValue.Make(@S16, TypeInfo(Int16), V1);
      S16 := High(Int16);
      TValue.Make(@S16, TypeInfo(Int16), V2);
      Assert(Rtti.Invoke(@TestS16, [V1, V2], ccReg, TypeInfo(Int16),
        True, False).AsInteger = High(Int16));
      Assert(Rtti.Invoke(@TestManyS16, [Int16(11), Int16(22), Int16(33), Int16(44),
        Int16(55), Int16(66), Int16(77), Int16(88), Int16(Low(Int16)),
        Int16(High(Int16))], ccReg, TypeInfo(Int16),
        True, False).AsInteger = High(Int16));
    
      { U32 }
    
      U32 := Low(UInt32);
      TValue.Make(@U32, TypeInfo(UInt32), V1);
      U32 := High(UInt32);
      TValue.Make(@U32, TypeInfo(UInt32), V2);
      Assert(Rtti.Invoke(@TestU32, [V1, V2], ccReg, TypeInfo(UInt32),
        True, False).AsInt64 = High(UInt32));
      Assert(Rtti.Invoke(@TestManyU32, [UInt32(11), UInt32(22), UInt32(33),
        UInt32(44), UInt32(55), UInt32(66), UInt32(77), UInt32(88),
        UInt32(Low(UInt32)), UInt32(High(UInt32))], ccReg, TypeInfo(UInt32),
        True, False).AsInt64 = High(UInt32));
    
      { S32 }
    
      S32 := Low(Int32);
      TValue.Make(@S32, TypeInfo(Int32), V1);
      S32 := High(Int32);
      TValue.Make(@S32, TypeInfo(Int32), V2);
      Assert(Rtti.Invoke(@TestS32, [V1, V2], ccReg, TypeInfo(Int32),
        True, False).AsInt64 = High(Int32));
      Assert(Rtti.Invoke(@TestManyS32, [Int32(11), Int32(22), Int32(33),
        Int32(44), Int32(55), Int32(66), Int32(77), Int32(88),
        Int32(Low(Int32)), Int32(High(Int32))], ccReg, TypeInfo(Int32),
        True, False).AsInt64 = High(Int32));
    
      { U64 }
    
      U64 := Low(UInt64);
      TValue.Make(@U64, TypeInfo(UInt64), V1);
      U64 := High(UInt64);
      TValue.Make(@U64, TypeInfo(UInt64), V2);
      Assert(Rtti.Invoke(@TestU64, [V1, V2], ccReg, TypeInfo(UInt64),
        True, False).AsUInt64 = High(UInt64));
      Assert(Rtti.Invoke(@TestManyU64, [UInt64(11), UInt64(22), UInt64(33),
        UInt64(44), UInt64(55), UInt64(66), UInt64(77), UInt64(88),
        UInt64(Low(UInt64)), UInt64(High(UInt64))], ccReg, TypeInfo(UInt64),
        True, False).AsUInt64 = High(UInt64));
    
      { S64 }
    
      S64 := Low(Int64);
      TValue.Make(@S64, TypeInfo(Int64), V1);
      S64 := High(Int64);
      TValue.Make(@S64, TypeInfo(Int64), V2);
      Assert(Rtti.Invoke(@TestS64, [V1, V2], ccReg, TypeInfo(Int64),
        True, False).AsInt64 = High(Int64));
      Assert(Rtti.Invoke(@TestManyS64, [Int64(11), Int64(22), Int64(33),
        Int64(44), Int64(55), Int64(66), Int64(77), Int64(88),
        Int64(Low(Int64)), Int64(High(Int64))], ccReg, TypeInfo(Int64),
        True, False).AsInt64 = High(Int64));
    
      { F32 }
    
      F32 := MinSingle;
      TValue.Make(@F32, TypeInfo(Single), V1);
      F32 := MaxSingle;
      TValue.Make(@F32, TypeInfo(Single), V2);
      Assert(EqF32(Rtti.Invoke(@TestF32, [V1, V2], ccReg, TypeInfo(Single),
        True, False).AsExtended, MaxSingle));
      Assert(EqF32(Rtti.Invoke(@TestManyF32, [Single(11.11), Single(22.22),
        Single(33.33), Single(44.44), Single(55.55), Single(66.66), Single(77.77),
        Single(88.88), Single(MinSingle), Single(MaxSingle)], ccReg,
        TypeInfo(Single), True, False).AsExtended, MaxSingle));
    
      { F64 }
    
      F64 := MinDouble;
      TValue.Make(@F64, TypeInfo(Double), V1);
      F64 := MaxDouble;
      TValue.Make(@F64, TypeInfo(Double), V2);
      Assert(EqF64(Rtti.Invoke(@TestF64, [V1, V2], ccReg, TypeInfo(Double),
        True, False).AsExtended, MaxDouble));
      Assert(EqF64(Rtti.Invoke(@TestManyF64, [Double(11.11), Double(22.22),
        Double(33.33), Double(44.44), Double(55.55), Double(66.66), Double(77.77),
        Double(88.88), Double(MinDouble), Double(MaxDouble)], ccReg,
        TypeInfo(Double), True, False).AsExtended, MaxDouble));
    
      { FCO }
    
      FCO := 123;
      TValue.Make(@FCO, TypeInfo(Comp), V1);
      FCO := 456;
      TValue.Make(@FCO, TypeInfo(Comp), V2);
      Assert(Rtti.Invoke(@TestFCO, [V1, V2], ccReg, TypeInfo(Int64{Comp}),
        True, False).AsInt64 = 789);
    
      { FCU }
    
      FCU := MinCurrency;
      TValue.Make(@FCU, TypeInfo(Currency), V1);
      FCU := MaxCurrency;
      TValue.Make(@FCU, TypeInfo(Currency), V2);
      Assert(EqF64(Rtti.Invoke(@TestFCU, [V1, V2], ccReg, TypeInfo(Currency),
        True, False).AsCurrency, MaxCurrency));
      Assert(EqF64(Rtti.Invoke(@TestManyFCU, [Currency(11.11), Currency(22.22),
        Currency(33.33), Currency(44.44), Currency(55.55), Currency(66.66),
        Currency(77.77), Currency(88.88), Currency(MinCurrency),
        Currency(MaxCurrency)], ccReg, TypeInfo(Currency),
        True, False).AsCurrency, MaxCurrency));
    
      { F80 }
    
      F80 := MinExtended;
      TValue.Make(@F80, TypeInfo(Extended), V1);
      F80 := MaxExtended;
      TValue.Make(@F80, TypeInfo(Extended), V2);
      Assert(EqF80(Rtti.Invoke(@TestF80, [V1, V2], ccReg, TypeInfo(Extended),
        True, False).AsExtended, MaxExtended));
      Assert(EqF80(Rtti.Invoke(@TestManyF80, [Extended(11.11), Extended(22.22),
        Extended(33.33), Extended(44.44), Extended(55.55), Extended(66.66),
        Extended(77.77), Extended(88.88), Extended(MinExtended),
        Extended(MaxExtended)], ccReg, TypeInfo(Extended),
        True, False).AsExtended, MaxExtended));
    
      { SS }
    
      SS := 'abc';
      TValue.Make(@SS[0], TypeInfo(ShortString), V1);
      SS := '123';
      TValue.Make(@SS[0], TypeInfo(ShortString), V2);
      Assert(Rtti.Invoke(@TestSS, [V1, V2], ccReg, TypeInfo(ShortString),
        True, False).AsString = 'abc123');
      SetLength(VA, 10);
      for I := Low(VA) to High(VA) do
      begin
        SS := Concat('S', I.ToString);
        TValue.Make(@SS[0], TypeInfo(ShortString), VA[I]);
      end;
      Assert(Rtti.Invoke(@TestManySS, VA, ccReg, TypeInfo(ShortString),
        True, False).AsString = 'abc123');
    
      { SA }
    
      SA := 'abc';
      TValue.Make(@SA, TypeInfo(AnsiString), V1);
      SA := '123';
      TValue.Make(@SA, TypeInfo(AnsiString), V2);
      Assert(Rtti.Invoke(@TestSA, [V1, V2], ccReg, TypeInfo(AnsiString),
        True, False).AsAnsiString = 'abc123');
      SetLength(VA, 10);
      for I := Low(VA) to High(VA) do
      begin
        SA := Concat('S', I.ToString);
        TValue.Make(@SA, TypeInfo(AnsiString), VA[I]);
      end;
      Assert(Rtti.Invoke(@TestManySA, VA, ccReg, TypeInfo(AnsiString),
        True, False).AsAnsiString = 'abc123');
    
      { SU }
    
      SU := 'abc';
      TValue.Make(@SU, TypeInfo(UnicodeString), V1);
      SU := '123';
      TValue.Make(@SU, TypeInfo(UnicodeString), V2);
      Assert(Rtti.Invoke(@TestSU, [V1, V2], ccReg, TypeInfo(UnicodeString),
        True, False).AsUnicodeString = 'abc123');
      SetLength(VA, 10);
      for I := Low(VA) to High(VA) do
      begin
        SU := Concat('S', UnicodeString(I.ToString));
        TValue.Make(@SU, TypeInfo(UnicodeString), VA[I]);
      end;
      Assert(Rtti.Invoke(@TestManySU, VA, ccReg, TypeInfo(UnicodeString),
        True, False).AsUnicodeString = 'abc123');
    
      { SW }
    
      SW := 'abc';
      TValue.Make(@SW, TypeInfo(WideString), V1);
      SW := '123';
      TValue.Make(@SW, TypeInfo(WideString), V2);
      Assert(Rtti.Invoke(@TestSW, [V1, V2], ccReg, TypeInfo(WideString),
        True, False).AsString = 'abc123');
      SetLength(VA, 10);
      for I := Low(VA) to High(VA) do
      begin
        SW := Concat('S', WideString(I.ToString));
        TValue.Make(@SW, TypeInfo(WideString), VA[I]);
      end;
      Assert(Rtti.Invoke(@TestManySW, VA, ccReg, TypeInfo(WideString),
        True, False).AsString = 'abc123');
    
      { P }
    
      P := @GVal1;
      TValue.Make(@P, TypeInfo(Pointer), V1);
      P := @GVal2;
      TValue.Make(@P, TypeInfo(Pointer), V2);
      Assert(Pointer(Rtti.Invoke(@TestP, [V1, V2], ccReg, TypeInfo(Int64{Pointer}),
        True, False).GetReferenceToRawData^) = @GVal3);
      Assert(Pointer(Rtti.Invoke(@TestManyP, [@GVal1, @GVal2, @GVal3, @GVal1,
        @GVal2, @GVal3, @GVal1, @GVal2, @GVal3, @GVal1], ccReg, TypeInfo(Int64),
        True, False).GetReferenceToRawData^) = @GVal3);
    
      { AD }
    
      AD := [123, 456];
      TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V1);
      AD := nil;
      SetLength(AD, 3);
      AD[0] := 789;
      AD[1] := 654;
      AD[2] := 321;
      TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V2);
      Assert(EqArr(TArrayOfLongIntDyn(Rtti.Invoke(@TestAD, [V1, V2], ccReg,
        TypeInfo(TArrayOfLongIntDyn), True, False).GetReferenceToRawData^),
        [147, 258, 369]));
    
      AD := [123, 456];
      TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V1);
      AD := [789, 654, 321];
      TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V2);
      Assert(EqArr(TArrayOfLongIntDyn(Rtti.Invoke(@TestManyAD, [V1, V2, V1, V2, V1,
        V2, V1, V2, V1, V2], ccReg, TypeInfo(TArrayOfLongIntDyn),
        True, False).GetReferenceToRawData^), [963, 852, 741]));
    
      { AS }
    
      AS_[0] := 123;
      AS_[1] := 456;
      AS_[2] := 0;
      TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V1);
      AS_[0] := 789;
      AS_[1] := 654;
      AS_[2] := 321;
      TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V2);
      Assert(EqArr(TArrayOfLongIntStatic(Rtti.Invoke(@TestAS, [V1, V2], ccReg,
        TypeInfo(TArrayOfLongIntStatic), True, False).GetReferenceToRawData^),
        [147, 258, 369]));
    
      AS_[0] := 123;
      AS_[1] := 456;
      AS_[2] := 0;
      TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V1);
      AS_[0] := 789;
      AS_[1] := 654;
      AS_[2] := 321;
      TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V2);
      Assert(EqArr(TArrayOfLongIntStatic(Rtti.Invoke(@TestManyAS, [V1, V2, V1, V2,
        V1, V2, V1, V2, V1, V2], ccReg, TypeInfo(TArrayOfLongIntStatic),
        True, False).GetReferenceToRawData^), [963, 852, 741]));
    
      { E }
    
      E := Low(TTestEnum);
      TValue.Make(@E, TypeInfo(TTestEnum), V1);
      E := High(TTestEnum);
      TValue.Make(@E, TypeInfo(TTestEnum), V2);
      Assert(TTestEnum(Rtti.Invoke(@TestE, [V1, V2], ccReg, TypeInfo(TTestEnum),
        True, False).GetReferenceToRawData^) = High(TTestEnum));
      Assert(TTestEnum(Rtti.Invoke(@TestManyE, [V1, V2, V1, V2, V1, V2, V1, V2,
        V1, V2], ccReg, TypeInfo(TTestEnum), True, False).GetReferenceToRawData^) =
        High(TTestEnum));
    
      { S }
    
      S := [te1];
      TValue.Make(@S, TypeInfo(TTestSet), V1);
      S := [te10];
      TValue.Make(@S, TypeInfo(TTestSet), V2);
      Assert(TTestSet(Rtti.Invoke(@TestS, [V1, V2], ccReg, TypeInfo(TTestSet),
        True, False).GetReferenceToRawData^) = [te1, te2, te3]);
      Assert(TTestSet(Rtti.Invoke(@TestManyS, [V1, V2, V1, V2, V1, V2, V1, V2,
        V1, V2], ccReg, TypeInfo(TTestSet), True, False).GetReferenceToRawData^) =
        [te8, te9, te10]);
    
      { Static proc }
    
      Proc := @TestProcedure1;
      TValue.Make(@@Proc, TypeInfo(TTestProc), V1);
      Proc := @TestProcedure2;
      TValue.Make(@@Proc, TypeInfo(TTestProc), V2);
      GProcOK1 := False;
      GProcOK2 := True;
      Proc := TTestProc(Rtti.Invoke(@TestProc, [V1, V2], ccReg, TypeInfo(TTestProc),
        True, False).GetReferenceToRawData^);
      Assert(GProcOK1);
      Assert(not GProcOK2);
      GProcOK1 := False;
      Proc;
      Assert(GProcOK1);
      GProcOK1 := False;
      GProcOK2 := True;
      Proc := TTestProc(Rtti.Invoke(@TestManyProc, [V1, V2, V1, V2, V1, V2, V1, V2,
        V1, V2], ccReg, TypeInfo(TTestProc), True, False).GetReferenceToRawData^);
      Assert(GProcOK1);
      Assert(not GProcOK2);
      GProcOK1 := False;
      Proc;
      Assert(GProcOK1);
    
      { Static func 1 }
    
      Func1 := @TestFunction1;
      TValue.Make(@@Func1, TypeInfo(TTestFunc1), V1);
      Func1 := @TestFunction2;
      TValue.Make(@@Func1, TypeInfo(TTestFunc1), V2);
      Func1 := TTestFunc1(Rtti.Invoke(@TestFunc1, [V1, V2], ccReg,
        TypeInfo(TTestFunc1), True, False).GetReferenceToRawData^);
      Assert(Func1 = 123);
      Func1 := TTestFunc1(Rtti.Invoke(@TestManyFunc1, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(TTestFunc1), True,
        False).GetReferenceToRawData^);
      Assert(Func1 = 456);
    
      { Static func 2 }
    
      Func2 := @TestFunction3;
      TValue.Make(@@Func2, TypeInfo(TTestFunc2), V1);
      Func2 := @TestFunction4;
      TValue.Make(@@Func2, TypeInfo(TTestFunc2), V2);
      Func2 := TTestFunc2(Rtti.Invoke(@TestFunc2, [V1, V2], ccReg,
        TypeInfo(TTestFunc2), True, False).GetReferenceToRawData^);
      Assert(Func2(123, [456, 789]) = '123456789');
      Func2 := TTestFunc2(Rtti.Invoke(@TestManyFunc2, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(TTestFunc2), True,
        False).GetReferenceToRawData^);
      Assert(Func2(123, [456, 789]) = '123456789abc123');
    
      { Method }
    
      TestMethodObj := TTestMethodObj.Create;
      try
        Method1 := TestMethodObj.TestMethod1;
        TValue.Make(@@Method1, TypeInfo(TTestMethod1), V1);
        Method1 := TestMethodObj.TestMethod2;
        TValue.Make(@@Method1, TypeInfo(TTestMethod1), V2);
        GProcOK1 := False;
        GProcOK2 := True;
        Method1 := TTestMethod1(Rtti.Invoke(@TestMethod1, [TestMethodObj, V1, V2],
          ccReg, TypeInfo(TTestMethod1), True, False).GetReferenceToRawData^);
        Assert(GProcOK1);
        Assert(not GProcOK2);
        GProcOK1 := False;
        Method1;
        Assert(GProcOK1);
        GProcOK1 := False;
        GProcOK2 := True;
        Method1 := TTestMethod1(Rtti.Invoke(@TestManyMethod1, [TestMethodObj, V1,
          V2, V1, V2, V1, V2, V1, V2, V1, V2], ccReg, TypeInfo(TTestMethod1),
          True, False).GetReferenceToRawData^);
        Assert(GProcOK1);
        Assert(not GProcOK2);
        GProcOK1 := False;
        Method1;
        Assert(GProcOK1);
      finally
        TestMethodObj.Destroy;
      end;
    
      { R1 }
    
      R1.Member1 := 123;
      R1.Member2 := 456;
      TValue.Make(@R1, TypeInfo(TTestRecord1), V1);
      R1.Member1 := 987;
      R1.Member2 := 654;
      TValue.Make(@R1, TypeInfo(TTestRecord1), V2);
      R1 := TTestRecord1(Rtti.Invoke(@TestRecord1, [V1, V2], ccReg,
        TypeInfo(TTestRecord1), True, False).GetReferenceToRawData^);
      Assert((R1.Member1 = 147) and (R1.Member2 = 258));
      R1 := TTestRecord1(Rtti.Invoke(@TestManyRecord1, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(TTestRecord1), True,
        False).GetReferenceToRawData^);
      Assert((R1.Member1 = 258) and (R1.Member2 = 147));
    
      { R2 }
    
      R2.Member1 := 'abc';
      R2.Member2 := 123;
      TValue.Make(@R2, TypeInfo(TTestRecord2), V1);
      R2.Member1 := 'def';
      R2.Member2 := 456;
      TValue.Make(@R2, TypeInfo(TTestRecord2), V2);
      R2 := TTestRecord2(Rtti.Invoke(@TestRecord2, [V1, V2], ccReg,
        TypeInfo(TTestRecord2), True, False).GetReferenceToRawData^);
      Assert((R2.Member1 = 'ghi') and (R2.Member2 = 789));
      R2 := TTestRecord2(Rtti.Invoke(@TestManyRecord2, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(TTestRecord2), True,
        False).GetReferenceToRawData^);
      Assert((R2.Member1 = 'xyz') and (R2.Member2 = 951));
    
      { R3 }
    
      R3.Member1 := 11;
      R3.Member2 := 22.22;
      R3.Member3 := 'aaa';
      R3.Member4 := 33.33;
      TValue.Make(@R3, TypeInfo(TTestRecord3), V1);
      R3.Member1 := 22;
      R3.Member2 := 44.44;
      R3.Member3 := 'bbb';
      R3.Member4 := 55.55;
      TValue.Make(@R3, TypeInfo(TTestRecord3), V2);
      R3 := TTestRecord3(Rtti.Invoke(@TestRecord3, [V1, V2], ccReg,
        TypeInfo(TTestRecord3), True, False).GetReferenceToRawData^);
      Assert((R3.Member1 = 123) and EqF64(R3.Member2, 12.34) and
        (R3.Member3 = 'abc') and EqF80(R3.Member4, 45.67));
      R3 := TTestRecord3(Rtti.Invoke(@TestManyRecord3, [V1, V2, V1, V2, V1, V2,
        V1, V2, V1, V2], ccReg, TypeInfo(TTestRecord3), True,
        False).GetReferenceToRawData^);
      Assert((R3.Member1 = 321) and EqF64(R3.Member2, 43.21) and
        (R3.Member3 = 'def') and EqF80(R3.Member4, 76.54));
    
      { C1 }
    
      C1 := TTestClass1.Create;
      C12 := TTestClass1.Create;
      try
        C1.Field1 := 123;
        C1.Field2 := 456;
        TValue.Make(@C1, TypeInfo(TTestClass1), V1);
        C12.Field1 := 987;
        C12.Field2 := 654;
        TValue.Make(@C12, TypeInfo(TTestClass1), V2);
        C1R := TTestClass1(Rtti.Invoke(@TestClass1, [V1, V2], ccReg,
          TypeInfo(TTestClass1), True, False).GetReferenceToRawData^);
        try
          Assert((C1R.Field1 = 147) and (C1R.Field2 = 258));
        finally
          C1R.Free;
        end;
        C1R := TTestClass1(Rtti.Invoke(@TestManyClass1, [V1, V2, V1, V2, V1, V2, V1,
          V2, V1, V2], ccReg, TypeInfo(TTestClass1), True,
          False).GetReferenceToRawData^);
        try
          Assert((C1R.Field1 = 258) and (C1R.Field2 = 147));
        finally
          C1R.Free;
        end;
      finally
        C1.Destroy;
        C12.Destroy;
      end;
    
      { C2 }
    
      C2 := TTestClass2.Create;
      C22 := TTestClass2.Create;
      try
        C2.Field1 := 'abc';
        C2.Field2 := 123;
        TValue.Make(@C2, TypeInfo(TTestClass2), V1);
        C22.Field1 := 'def';
        C22.Field2 := 456;
        TValue.Make(@C22, TypeInfo(TTestClass2), V2);
        C2R := TTestClass2(Rtti.Invoke(@TestClass2, [V1, V2], ccReg,
          TypeInfo(TTestClass2), True, False).GetReferenceToRawData^);
        try
          Assert((C2R.Field1 = 'ghi') and (C2R.Field2 = 789));
        finally
          C2R.Free;
        end;
        C2R := TTestClass2(Rtti.Invoke(@TestManyClass2, [V1, V2, V1, V2, V1, V2, V1,
          V2, V1, V2], ccReg, TypeInfo(TTestClass2), True,
          False).GetReferenceToRawData^);
        try
          Assert((C2R.Field1 = 'xyz') and (C2R.Field2 = 951));
        finally
          C2R.Free;
        end;
      finally
        C2.Destroy;
        C22.Destroy;
      end;
    
      { C3 }
    
      C3 := TTestClass3.Create;
      C32 := TTestClass3.Create;
      try
        C3.Field1 := 11;
        C3.Field2 := 22.22;
        C3.Field3 := 'aaa';
        C3.Field4 := 33.33;
        TValue.Make(@C3, TypeInfo(TTestClass3), V1);
        C32.Field1 := 22;
        C32.Field2 := 44.44;
        C32.Field3 := 'bbb';
        C32.Field4 := 55.55;
        TValue.Make(@C32, TypeInfo(TTestClass3), V2);
        C3R := TTestClass3(Rtti.Invoke(@TestClass3, [V1, V2], ccReg,
          TypeInfo(TTestClass3), True, False).GetReferenceToRawData^);
        try
          Assert((C3R.Field1 = 123) and EqF64(C3R.Field2, 12.34) and
            (C3R.Field3 = 'abc') and EqF80(C3R.Field4, 45.67));
        finally
          C3R.Free;
        end;
        C3R := TTestClass3(Rtti.Invoke(@TestManyClass3, [V1, V2, V1, V2, V1, V2,
          V1, V2, V1, V2], ccReg, TypeInfo(TTestClass3), True,
          False).GetReferenceToRawData^);
        try
          Assert((C3R.Field1 = 321) and EqF64(C3R.Field2, 43.21) and
            (C3R.Field3 = 'def') and EqF80(C3R.Field4, 76.54));
        finally
          C3R.Free
        end;
      finally
        C3.Destroy;
        C32.Destroy;
      end;
    
      { I1 }
    
      I1 := TTestIntf1.Create;
      I1.Field1 := 123;
      I1.Field2 := 456;
      TValue.Make(@I1, TypeInfo(ITestIntf1), V1);
      I12 := TTestIntf1.Create;
      I12.Field1 := 987;
      I12.Field2 := 654;
      TValue.Make(@I12, TypeInfo(ITestIntf1), V2);
      I1R := ITestIntf1(Rtti.Invoke(@TestIntf1, [V1, V2], ccReg,
        TypeInfo(ITestIntf1), True, False).GetReferenceToRawData^);
      Assert((I1R.Field1 = 147) and (I1R.Field2 = 258));
      I1R := ITestIntf1(Rtti.Invoke(@TestManyIntf1, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(ITestIntf1), True,
        False).GetReferenceToRawData^);
      Assert((I1R.Field1 = 258) and (I1R.Field2 = 147));
    
      { I2 }
    
      I2 := TTestIntf2.Create;
      I2.Field1 := 'abc';
      I2.Field2 := 123;
      TValue.Make(@I2, TypeInfo(ITestIntf2), V1);
      I22 := TTestIntf2.Create;
      I22.Field1 := 'def';
      I22.Field2 := 456;
      TValue.Make(@I22, TypeInfo(ITestIntf2), V2);
      I2R := ITestIntf2(Rtti.Invoke(@TestIntf2, [V1, V2], ccReg,
        TypeInfo(ITestIntf2), True, False).GetReferenceToRawData^);
      Assert((I2R.Field1 = 'ghi') and (I2R.Field2 = 789));
      I2R := ITestIntf2(Rtti.Invoke(@TestManyIntf2, [V1, V2, V1, V2, V1, V2, V1,
        V2, V1, V2], ccReg, TypeInfo(ITestIntf2), True,
        False).GetReferenceToRawData^);
      Assert((I2R.Field1 = 'xyz') and (I2R.Field2 = 951));
    
      { I3 }
    
      I3 := TTestIntf3.Create;
      I3.Field1 := 11;
      I3.Field2 := 22.22;
      I3.Field3 := 'aaa';
      I3.Field4 := 33.33;
      TValue.Make(@I3, TypeInfo(ITestIntf3), V1);
      I32 := TTestIntf3.Create;
      I32.Field1 := 22;
      I32.Field2 := 44.44;
      I32.Field3 := 'bbb';
      I32.Field4 := 55.55;
      TValue.Make(@I32, TypeInfo(ITestIntf3), V2);
      I3R := ITestIntf3(Rtti.Invoke(@TestIntf3, [V1, V2], ccReg,
        TypeInfo(ITestIntf3), True, False).GetReferenceToRawData^);
      Assert((I3R.Field1 = 123) and EqF64(I3R.Field2, 12.34) and
        (I3R.Field3 = 'abc') and EqF80(I3R.Field4, 45.67));
      I3R := ITestIntf3(Rtti.Invoke(@TestManyIntf3, [V1, V2, V1, V2, V1, V2,
        V1, V2, V1, V2], ccReg, TypeInfo(ITestIntf3), True,
        False).GetReferenceToRawData^);
      Assert((I3R.Field1 = 321) and EqF64(I3R.Field2, 43.21) and
        (I3R.Field3 = 'def') and EqF80(I3R.Field4, 76.54));
    
      WriteLn('All tests OK');
    end.
    
    tests.pp (59,385 bytes)
  • rtti-sysv-invoke.patch (23,312 bytes)
    ��diff --git a/packages/rtl-objpas/src/inc/rtti.pp b/packages/rtl-objpas/src/inc/rtti.pp
    
    index d1e42241..8cab3462 100644
    
    --- a/packages/rtl-objpas/src/inc/rtti.pp
    
    +++ b/packages/rtl-objpas/src/inc/rtti.pp
    
    @@ -3552,7 +3552,7 @@ begin
    
     end;}
    
     
    
     {$ifndef InLazIDE}
    
    -{$if defined(CPUX86_64) and defined(WIN64)}
    
    +{$if defined(CPUX86_64)}
    
     {$I invoke.inc}
    
     {$endif}
    
     {$endif}
    
    diff --git a/packages/rtl-objpas/src/x86_64/invoke.inc b/packages/rtl-objpas/src/x86_64/invoke.inc
    
    index b64c2a78..c155de43 100644
    
    --- a/packages/rtl-objpas/src/x86_64/invoke.inc
    
    +++ b/packages/rtl-objpas/src/x86_64/invoke.inc
    
    @@ -1,7 +1,8 @@
    
     {
    
       This file is part of the Free Pascal run time library.
    
    -  Copyright (C) 2018 Sven Barth
    
    -  member of the Free Pascal development team.
    
    +  Copyright (C) 2018 Sven Barth (Win64)
    
    +  Copyright (C) 2018 Silvio Clecio (silvioprog) (Sys V)
    
    +  members of the Free Pascal development team.
    
     
    
       Function call manager for x86_64
    
     
    
    @@ -80,6 +81,166 @@ asm
    
       movq 16(%rsp), %rsi
    
       movq 8(%rsp), %rbp
    
     end;
    
    +{$else}
    
    +const
    
    +  FPREGS_COUNT = 8;
    
    +  GPREGS_COUNT = 6;
    
    +
    
    +  INTEGER_CLASS = 0;
    
    +  SSE_CLASS = 1;
    
    +  X87_CLASS = 2;
    
    +  MEMORY_CLASS = 3;
    
    +
    
    +type
    
    +  TStackItem = packed record
    
    +    Value: PtrUInt; // 0-8
    
    +    Size: SizeInt; // 8-16
    
    +    ByRef: Boolean; // 16-17
    
    +    ArgCls: Byte; // 17-18
    
    +  end;
    
    +
    
    +  TResultType = type Byte;
    
    +
    
    +  TInvokeHandle = packed record
    
    +    FPRegs: array[0..Pred(FPREGS_COUNT)] of PtrUInt; // 0-64
    
    +    GPRegs: array[0..Pred(GPREGS_COUNT)] of PtrUInt; // 64-112
    
    +    StackAddr: Pointer; // 112-120
    
    +    StackCount: PtrUInt; // 120-128
    
    +    StackSize: SizeUInt; // 128-136
    
    +    FuncAddr: CodePointer; // 136-144
    
    +    ResultType: TResultType; // 1144-145
    
    +    Result: PtrUInt; // 145-153
    
    +  end;
    
    +
    
    +procedure InvokeKernelSysV(var AHandle: TInvokeHandle); assembler; nostackframe;
    
    +label
    
    +  get_stack_item, check_stack_items, get_extra_arg, check_extra_args, by_ref,
    
    +    mem_cls, exit;
    
    +asm
    
    +  { save base pointer; set new base pointer to rsp }
    
    +  push %rbp
    
    +  mov %rsp, %rbp
    
    +
    
    +  { save callee-saved registers }
    
    +  push %rbx
    
    +  push %r12
    
    +  push %r13
    
    +  push %r14
    
    +  push %r15
    
    +
    
    +  { save invoke handle }
    
    +  mov %rdi, %r15
    
    +
    
    +  { allocate extra arguments space }
    
    +  mov 128(%r15), %rcx // Handle.StackSize
    
    +  sub %rcx, %rsp
    
    +
    
    +  { iterate stack items }
    
    +  mov 112(%r15), %rsi // Handle.StackAddr
    
    +  mov $-8, %r13
    
    +  mov $-18, %rax
    
    +  mov 120(%r15), %rcx // Handle.StackCount
    
    +  jmp check_stack_items
    
    +get_stack_item:
    
    +  add $18, %rax
    
    +  lea (%rsi, %rax), %rdi
    
    +  mov (%rdi), %r9 // Item.Value
    
    +  mov 8(%rdi), %r8 // Item.Size
    
    +  movb 16(%rdi), %r12b // Item.ByRef
    
    +  movb 17(%rdi), %dl // Item.ArgCls
    
    +  { get stack item values }
    
    +  mov $-8, %r14
    
    +  jmp check_extra_args
    
    +get_extra_arg:
    
    +  add $8, %r13
    
    +  add $8, %r14
    
    +  cmpb $1, %r12b
    
    +  je by_ref
    
    +  { INTEGER/SSE CLASS }
    
    +  lea (%r9, %r14), %rbx
    
    +  mov %rbx, (%rsp, %r13)
    
    +  dec %rcx
    
    +  jmp check_stack_items
    
    +by_ref:
    
    +  cmpb MEMORY_CLASS, %dl
    
    +  je mem_cls
    
    +  { POINTER/X87 CLASS }
    
    +  mov (%r9, %r14), %rbx
    
    +  mov %rbx, (%rsp, %r13)
    
    +  sub $8, %r8
    
    +  jmp check_extra_args
    
    +mem_cls:
    
    +  { MEMORY CLASS }
    
    +  sub $8, %r8
    
    +  mov (%r9, %r14), %rbx
    
    +  mov %rbx, (%rsp, %r13)
    
    +  cmp $0, %r8
    
    +  jg get_extra_arg
    
    +  dec %rcx
    
    +  jmp check_stack_items
    
    +check_extra_args:
    
    +  cmp $0, %r8
    
    +  jge get_extra_arg
    
    +  dec %rcx
    
    +check_stack_items:
    
    +  cmp $0, %rcx
    
    +  jnz get_stack_item
    
    +
    
    +  { setup general purpose registers }
    
    +  lea 64(%r15), %rbx // Handle.GPRegs
    
    +  mov (%rbx), %rdi
    
    +  mov 8(%rbx), %rsi
    
    +  mov 16(%rbx), %rdx
    
    +  mov 24(%rbx), %rcx
    
    +  mov 32(%rbx), %r8
    
    +  mov 40(%rbx), %r9
    
    +
    
    +  { setup floating point registers }
    
    +  lea (%r15), %rbx // Handle.FPRegs
    
    +  movq (%rbx), %xmm0
    
    +  movq 8(%rbx), %xmm1
    
    +  movq 16(%rbx), %xmm2
    
    +  movq 24(%rbx), %xmm3
    
    +  movq 32(%rbx), %xmm4
    
    +  movq 40(%rbx), %xmm5
    
    +  movq 48(%rbx), %xmm6
    
    +  movq 56(%rbx), %xmm7
    
    +
    
    +  { invoke user function }
    
    +  mov $0, %rax
    
    +  call *136(%r15) // Handle.FuncAddr
    
    +
    
    +  { get result type }
    
    +  movb 144(%r15), %cl // Handle.ResultType
    
    +
    
    +  { return 64-bit value }
    
    +  mov %rax, 145(%r15) // Handle.Result
    
    +  cmpb INTEGER_CLASS, %cl
    
    +  je exit
    
    +
    
    +  { return 80-bit float }
    
    +  cmpb X87_CLASS, %cl
    
    +  jne exit
    
    +  fstpt 145(%r15) // Handle.Result
    
    +
    
    +exit:
    
    +
    
    +  { deallocate extra arguments space }
    
    +  mov 128(%r15), %rcx // Handle.StackSize
    
    +  add %rcx, %rsp
    
    +
    
    +  { restore callee-saved registers }
    
    +  pop %r15
    
    +  pop %r14
    
    +  pop %r13
    
    +  pop %r12
    
    +  pop %rbx
    
    +
    
    +  { reset stack to base pointer; restore old base pointer }
    
    +  leave
    
    +  { return to caller }
    
    +  ret
    
    +end;
    
     {$endif}
    
     
    
     resourcestring
    
    @@ -131,6 +292,7 @@ type
    
       PBoolean64 = ^Boolean64;
    
       PByteBool = ^ByteBool;
    
       PQWordBool = ^QWordBool;
    
    +{$ifdef windows}
    
     var
    
       stackarea: array of PtrUInt;
    
       stackptr: Pointer;
    
    @@ -140,6 +302,132 @@ var
    
       td: PTypeData;
    
       retinparam: Boolean;
    
       argcount, resreg: SizeInt;
    
    +{$else}
    
    +var
    
    +  VArg: TFunctionCallParameter;
    
    +  VHandle: TInvokeHandle;
    
    +  VStack: array of TStackItem;
    
    +  VRetReg, VRetSize: SizeInt;
    
    +  I, VGPRegIdx, VFPRegIdx: LongInt;
    
    +  VRetInParam: Boolean;
    
    +
    
    +  function PushRegVal(AValue: PtrUInt;
    
    +    AArgCls: Byte = INTEGER_CLASS): Boolean;
    
    +  begin
    
    +    case AArgCls of
    
    +      INTEGER_CLASS:
    
    +      begin
    
    +        if VGPRegIdx = VRetReg then
    
    +          Inc(VGPRegIdx);
    
    +        if VGPRegIdx < GPREGS_COUNT then
    
    +        begin
    
    +          VHandle.GPRegs[VGPRegIdx] := AValue;
    
    +          Inc(VGPRegIdx);
    
    +          Inc(VHandle.StackSize, 8);
    
    +          Exit(True);
    
    +        end;
    
    +      end;
    
    +      SSE_CLASS:
    
    +        if VFPRegIdx < FPREGS_COUNT then
    
    +        begin
    
    +          VHandle.FPRegs[VFPRegIdx] := AValue;
    
    +          Inc(VFPRegIdx);
    
    +          Inc(VHandle.StackSize, 8);
    
    +          Exit(True);
    
    +        end;
    
    +    end;
    
    +    Result := False;
    
    +  end;
    
    +
    
    +  procedure PushStackVal(AValue: PtrUInt; ASize: SizeInt;
    
    +    AByRef: Boolean = False; AArgCls: Byte = INTEGER_CLASS);
    
    +  var
    
    +    VStackItem: TStackItem;
    
    +  begin
    
    +    Inc(VHandle.StackCount);
    
    +    SetLength(VStack, VHandle.StackCount);
    
    +    VStackItem.ByRef := AByRef;
    
    +    VStackItem.Size := ASize;
    
    +    VStackItem.Value := AValue;
    
    +    VStackItem.ArgCls := AArgCls;
    
    +    VStack[Pred(VHandle.StackCount)] := VStackItem;
    
    +    if AArgCls = MEMORY_CLASS then
    
    +      Inc(VHandle.StackSize, ASize)
    
    +    else
    
    +      Inc(VHandle.StackSize, 16); // stack aligned on 16 bytes boundary
    
    +  end;
    
    +
    
    +  function PushVal(AValue: PtrUInt; AArgCls: Byte = INTEGER_CLASS;
    
    +    AByRef: Boolean = False): Boolean; inline;
    
    +  begin
    
    +    Result := PushRegVal(AValue, AArgCls);
    
    +    if not Result then
    
    +      PushStackVal(AValue, 8, AByRef);
    
    +  end;
    
    +
    
    +  function MapVal(ATD: PTypeData; AKind: TTypeKind;
    
    +    AValue: Pointer): Boolean;
    
    +  var
    
    +    VField: PManagedField;
    
    +    VTD: PTypeData;
    
    +    I: SizeUInt;
    
    +  begin
    
    +    Result := True;
    
    +    case AKind of
    
    +      tkInteger, tkEnumeration: PushVal(PtrUInt(AValue^));
    
    +      tkFloat:
    
    +        case ATD^.FloatType of
    
    +          ftSingle, ftDouble: PushVal(PtrUInt(AValue^), SSE_CLASS);
    
    +          ftExtended: PushStackVal(PtrUInt(AValue), 10, True);
    
    +          ftComp, ftCurr: PushVal(PtrUInt(AValue^));
    
    +        end;
    
    +      tkSet:
    
    +        if ATD^.SetSize <= 8 then
    
    +          PushVal(PtrUInt(AValue^))
    
    +        else
    
    +          if not PushRegVal(PtrUInt(AValue)) then
    
    +            PushStackVal(PtrUInt(AValue), ATD^.SetSize);
    
    +      tkMethod:
    
    +      begin
    
    +        PushRegVal(PtrUInt(AValue^));
    
    +        if not PushRegVal(PtrUInt(AValue^) + 8) then
    
    +          PushStackVal(PtrUInt(AValue), 8, True);
    
    +      end;
    
    +      tkSString: PushVal(PtrUInt(AValue));
    
    +      tkAString, tkWString, tkInterface, tkClass, tkObject, tkUString,
    
    +        tkDynArray, tkInterfaceRaw, tkProcVar, tkClassRef,
    
    +        tkPointer: PushVal(PtrUInt(AValue^));
    
    +      tkArray:
    
    +        if ATD^.ArrayData.Size <= 8 then
    
    +          PushVal(PtrUInt(AValue^))
    
    +        else
    
    +          if not PushRegVal(PtrUInt(AValue)) then
    
    +            PushStackVal(PtrUInt(AValue), ATD^.ArrayData.Size);
    
    +      tkRecord:
    
    +        if ATD^.RecSize <= 8 then
    
    +          PushVal(PtrUInt(AValue^))
    
    +        else
    
    +          if ATD^.RecSize <= 16 then
    
    +          begin
    
    +            VField := PManagedField(PByte(@ATD^.TotalFieldCount) + 4);
    
    +            for I := 0 to Pred(ATD^.TotalFieldCount) do
    
    +            begin
    
    +              VTD := GetTypeData(VField^.TypeRef);
    
    +              MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
    
    +              if ((VGPRegIdx = GPREGS_COUNT) or (VFPRegIdx = FPREGS_COUNT)) and
    
    +                (VHandle.StackCount = 0) then
    
    +                MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
    
    +              Inc(VField);
    
    +            end;
    
    +          end
    
    +          else
    
    +            PushStackVal(PtrUInt(AValue), ATD^.RecSize, True, MEMORY_CLASS);
    
    +      tkBool, tkInt64, tkQWord: PushVal(PtrUInt(AValue^));
    
    +    else
    
    +      Result := False;
    
    +    end;
    
    +  end;
    
    +{$endif}
    
     begin
    
       if Assigned(aResultType) and not Assigned(aResultValue) then
    
         raise EInvocationError.Create(SErrInvokeResultTypeNoValue);
    
    @@ -287,7 +575,57 @@ begin
    
         end;
    
       end;
    
     {$else}
    
    -  raise EInvocationError.Create(SErrPlatformNotSupported);
    
    +  VRetInParam := False;
    
    +  if Assigned(AResultType) then
    
    +  begin
    
    +    if not Assigned(AResultValue) then
    
    +      raise EInvocationError.Create(SErrInvokeResultTypeNoValue);
    
    +    case AResultType^.Kind of
    
    +      tkSString, tkAString, tkWString, tkArray, tkInterface, tkDynArray,
    
    +        tkUString: VRetInParam := True;
    
    +      tkRecord: VRetInParam := GetTypeData(AResultType)^.RecSize > 8;
    
    +    end;
    
    +  end;
    
    +  VStack := nil;
    
    +  VHandle := Default(TInvokeHandle);
    
    +  VGPRegIdx := 0;
    
    +  VFPRegIdx := 0;
    
    +  if VRetInParam then
    
    +  begin
    
    +    if fcfStatic in AFlags then
    
    +      VRetReg := 0
    
    +    else
    
    +      VRetReg := 1;
    
    +    VHandle.GPRegs[VRetReg] := PtrUInt(AResultValue);
    
    +  end
    
    +  else
    
    +    VRetReg := -1;
    
    +  for I := 0 to High(AArgs) do
    
    +  begin
    
    +    VArg := AArgs[I];
    
    +    if VArg.Info.ParamFlags * [pfArray, pfOut, pfVar, pfConstRef] <> [] then
    
    +      PushVal(PtrUInt(VArg.ValueRef))
    
    +    else
    
    +      if not MapVal(GetTypeData(VArg.Info.ParamType),
    
    +        VArg.Info.ParamType^.Kind, VArg.ValueRef) then
    
    +        raise EInvocationError.CreateFmt(SErrFailedToConvertArg,
    
    +          [I, VArg.Info.ParamType^.Name]);
    
    +  end;
    
    +  VHandle.StackAddr := @VStack[0];
    
    +  VHandle.FuncAddr := aCodeAddress;
    
    +  if AResultType = TypeInfo(Extended) then
    
    +  begin
    
    +    VHandle.ResultType := X87_CLASS;
    
    +    VRetSize := 10;
    
    +  end
    
    +  else
    
    +  begin
    
    +    VHandle.ResultType := INTEGER_CLASS;
    
    +    VRetSize := 8;
    
    +  end;
    
    +  InvokeKernelSysV(VHandle);
    
    +  if Assigned(AResultType) and not VRetInParam then
    
    +    Move(VHandle.Result, PPtrUInt(AResultValue)^, VRetSize);
    
     {$endif}
    
     end;
    
     
    
    diff --git a/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp b/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp
    
    index 90ddc3b8..c35e6243 100644
    
    --- a/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp
    
    +++ b/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp
    
    @@ -6,8 +6,11 @@ program testrunner.rtlobjpas;
    
     {$mode objfpc}{$H+}
    
     { Invoke needs a function call manager }
    
     {.$define useffi}
    
    -{$if defined(CPUX64) and defined(WINDOWS)}
    
    +{$if defined(CPUX64)}
    
     {$define testinvoke}
    
    +{$if defined(WINDOWS)}
    
    +{$define testimpl}
    
    +{$endif}
    
     {$else}
    
     {$ifdef useffi}
    
     {$define testinvoke}
    
    
    rtti-sysv-invoke.patch (23,312 bytes)

Activities

silvioprog

2018-12-21 08:34

reporter  

invoke_unix64.inc (10,172 bytes)
{
  This file is part of the Free Pascal run time library.
  Copyright (C) 2018 Silvio Clecio (silvioprog) member of the
  Free Pascal development team.

  Low-level invoke implementation for Linux x86_64

  See the file COPYING.FPC, included in this distribution,
  for details about the copyright.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}

{$IF DEFINED(CPUX86_64) AND DEFINED(UNIX)}
 {$DEFINE SYSTEM_HAS_INVOKE}
{$ENDIF}

{$IFDEF SYSTEM_HAS_INVOKE}

const
  FPREGS_COUNT = 8;
  GPREGS_COUNT = 6;

  INTEGER_CLASS = 0;
  SSE_CLASS = 1;
  X87_CLASS = 2;
  MEMORY_CLASS = 3;

type
  TStackItem = packed record
    Value: PtrUInt; // 0-8
    Size: SizeInt; // 8-16
    ByRef: Boolean; // 16-17
    ArgCls: Byte; // 17-18
  end;

  TResultType = type Byte;

  TInvokeHandle = packed record
    FPRegs: array[0..Pred(FPREGS_COUNT)] of PtrUInt; // 0-64
    GPRegs: array[0..Pred(GPREGS_COUNT)] of PtrUInt; // 64-112
    StackAddr: Pointer; // 112-120
    StackCount: PtrUInt; // 120-128
    StackSize: SizeUInt; // 128-136
    FuncAddr: CodePointer; // 136-144
    ResultType: TResultType; // 1144-145
    Result: PtrUInt; // 145-153
  end;

procedure InvokeKernelUnix64(var AHandle: TInvokeHandle); assembler; nostackframe;
label
  get_stack_item, check_stack_items, get_extra_arg, check_extra_args, by_ref,
    mem_cls, exit;
asm
  { save base pointer; set new base pointer to rsp }
  push %rbp
  mov %rsp, %rbp

  { save callee-saved registers }
  push %rbx
  push %r12
  push %r13
  push %r14
  push %r15

  { save invoke handle }
  mov %rdi, %r15

  { allocate extra arguments space }
  mov 128(%r15), %rcx // Handle.StackSize
  sub %rcx, %rsp

  { iterate stack items }
  mov 112(%r15), %rsi // Handle.StackAddr
  mov $-8, %r13
  mov $-18, %rax
  mov 120(%r15), %rcx // Handle.StackCount
  jmp check_stack_items
get_stack_item:
  add $18, %rax
  lea (%rsi, %rax), %rdi
  mov (%rdi), %r9 // Item.Value
  mov 8(%rdi), %r8 // Item.Size
  movb 16(%rdi), %r12b // Item.ByRef
  movb 17(%rdi), %dl // Item.ArgCls
  { get stack item values }
  mov $-8, %r14
  jmp check_extra_args
get_extra_arg:
  add $8, %r13
  add $8, %r14
  cmpb $1, %r12b
  je by_ref
  { INTEGER/SSE CLASS }
  lea (%r9, %r14), %rbx
  mov %rbx, (%rsp, %r13)
  dec %rcx
  jmp check_stack_items
by_ref:
  cmpb MEMORY_CLASS, %dl
  je mem_cls
  { POINTER/X87 CLASS }
  mov (%r9, %r14), %rbx
  mov %rbx, (%rsp, %r13)
  sub $8, %r8
  jmp check_extra_args
mem_cls:
  { MEMORY CLASS }
  sub $8, %r8
  mov (%r9, %r14), %rbx
  mov %rbx, (%rsp, %r13)
  cmp $0, %r8
  jg get_extra_arg
  dec %rcx
  jmp check_stack_items
check_extra_args:
  cmp $0, %r8
  jge get_extra_arg
  dec %rcx
check_stack_items:
  cmp $0, %rcx
  jnz get_stack_item

  { setup general purpose registers }
  lea 64(%r15), %rbx // Handle.GPRegs
  mov (%rbx), %rdi
  mov 8(%rbx), %rsi
  mov 16(%rbx), %rdx
  mov 24(%rbx), %rcx
  mov 32(%rbx), %r8
  mov 40(%rbx), %r9

  { setup floating point registers }
  lea (%r15), %rbx // Handle.FPRegs
  movq (%rbx), %xmm0
  movq 8(%rbx), %xmm1
  movq 16(%rbx), %xmm2
  movq 24(%rbx), %xmm3
  movq 32(%rbx), %xmm4
  movq 40(%rbx), %xmm5
  movq 48(%rbx), %xmm6
  movq 56(%rbx), %xmm7

  { invoke user function }
  mov $0, %rax
  call *136(%r15) // Handle.FuncAddr

  { get result type }
  movb 144(%r15), %cl // Handle.ResultType

  { return 64-bit value }
  mov %rax, 145(%r15) // Handle.Result
  cmpb INTEGER_CLASS, %cl
  je exit

  { return 80-bit float }
  cmpb X87_CLASS, %cl
  jne exit
  fstpt 145(%r15) // Handle.Result

exit:

  { deallocate extra arguments space }
  mov 128(%r15), %rcx // Handle.StackSize
  add %rcx, %rsp

  { restore callee-saved registers }
  pop %r15
  pop %r14
  pop %r13
  pop %r12
  pop %rbx

  { reset stack to base pointer; restore old base pointer }
  leave
  { return to caller }
  ret
end;

{$ENDIF}

resourcestring
{$IFDEF SYSTEM_HAS_INVOKE}
  SErrFailedToConvertArg = 'Failed to convert argument %d of type %s';
{$ELSE}
  SErrPlatformNotSupported = 'Invoke is not supported on this platform';
{$ENDIF}

procedure CallManagerInvoke(AFuncAddr: CodePointer;
  const AArgs: TFunctionCallParameterArray; ACallConv: TCallConv;
  AResultType: PTypeInfo; AResultValue: Pointer; AFlags: TFunctionCallFlags);
{$IFDEF SYSTEM_HAS_INVOKE}
var
  VArg: TFunctionCallParameter;
  VHandle: TInvokeHandle;
  VStack: array of TStackItem;
  VRetReg, VRetSize: SizeInt;
  I, VGPRegIdx, VFPRegIdx: LongInt;
  VRetInParam: Boolean;

  function PushRegVal(AValue: PtrUInt;
    AArgCls: Byte = INTEGER_CLASS): Boolean;
  begin
    case AArgCls of
      INTEGER_CLASS:
      begin
        if VGPRegIdx = VRetReg then
          Inc(VGPRegIdx);
        if VGPRegIdx < GPREGS_COUNT then
        begin
          VHandle.GPRegs[VGPRegIdx] := AValue;
          Inc(VGPRegIdx);
          Inc(VHandle.StackSize, 8);
          Exit(True);
        end;
      end;
      SSE_CLASS:
        if VFPRegIdx < FPREGS_COUNT then
        begin
          VHandle.FPRegs[VFPRegIdx] := AValue;
          Inc(VFPRegIdx);
          Inc(VHandle.StackSize, 8);
          Exit(True);
        end;
    end;
    Result := False;
  end;

  procedure PushStackVal(AValue: PtrUInt; ASize: SizeInt;
    AByRef: Boolean = False; AArgCls: Byte = INTEGER_CLASS);
  var
    VStackItem: TStackItem;
  begin
    Inc(VHandle.StackCount);
    SetLength(VStack, VHandle.StackCount);
    VStackItem.ByRef := AByRef;
    VStackItem.Size := ASize;
    VStackItem.Value := AValue;
    VStackItem.ArgCls := AArgCls;
    VStack[Pred(VHandle.StackCount)] := VStackItem;
    if AArgCls = MEMORY_CLASS then
      Inc(VHandle.StackSize, ASize)
    else
      Inc(VHandle.StackSize, 16); // stack aligned on 16 bytes boundary
  end;

  function PushVal(AValue: PtrUInt; AArgCls: Byte = INTEGER_CLASS;
    AByRef: Boolean = False): Boolean; inline;
  begin
    Result := PushRegVal(AValue, AArgCls);
    if not Result then
      PushStackVal(AValue, 8, AByRef);
  end;

  function MapVal(ATD: PTypeData; AKind: TTypeKind;
    AValue: Pointer): Boolean;
  var
    VField: PManagedField;
    VTD: PTypeData;
    I: SizeUInt;
  begin
    Result := True;
    case AKind of
      tkInteger, tkEnumeration: PushVal(PtrUInt(AValue^));
      tkFloat:
        case ATD^.FloatType of
          ftSingle, ftDouble: PushVal(PtrUInt(AValue^), SSE_CLASS);
          ftExtended: PushStackVal(PtrUInt(AValue), 10, True);
          ftComp, ftCurr: PushVal(PtrUInt(AValue^));
        end;
      tkSet:
        if ATD^.SetSize <= 8 then
          PushVal(PtrUInt(AValue^))
        else
          if not PushRegVal(PtrUInt(AValue)) then
            PushStackVal(PtrUInt(AValue), ATD^.SetSize);
      tkMethod:
      begin
        PushRegVal(PtrUInt(AValue^));
        if not PushRegVal(PtrUInt(AValue^) + 8) then
          PushStackVal(PtrUInt(AValue), 8, True);
      end;
      tkSString: PushVal(PtrUInt(AValue));
      tkAString, tkWString, tkInterface, tkClass, tkObject, tkUString,
        tkDynArray, tkInterfaceRaw, tkProcVar, tkClassRef,
        tkPointer: PushVal(PtrUInt(AValue^));
      tkArray:
        if ATD^.ArrayData.Size <= 8 then
          PushVal(PtrUInt(AValue^))
        else
          if not PushRegVal(PtrUInt(AValue)) then
            PushStackVal(PtrUInt(AValue), ATD^.ArrayData.Size);
      tkRecord:
        if ATD^.RecSize <= 8 then
          PushVal(PtrUInt(AValue^))
        else
          if ATD^.RecSize <= 16 then
          begin
            VField := PManagedField(PByte(@ATD^.TotalFieldCount) + 4);
            for I := 0 to Pred(ATD^.TotalFieldCount) do
            begin
              VTD := GetTypeData(VField^.TypeRef);
              MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
              if ((VGPRegIdx = GPREGS_COUNT) or (VFPRegIdx = FPREGS_COUNT)) and
                (VHandle.StackCount = 0) then
                MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);
              Inc(VField);
            end;
          end
          else
            PushStackVal(PtrUInt(AValue), ATD^.RecSize, True, MEMORY_CLASS);
      tkBool, tkInt64, tkQWord: PushVal(PtrUInt(AValue^));
    else
      Result := False;
    end;
  end;

{$ENDIF}
begin
{$IFDEF SYSTEM_HAS_INVOKE}
  VRetInParam := False;
  if Assigned(AResultType) then
  begin
    if not Assigned(AResultValue) then
      raise EInvocationError.Create(SErrInvokeResultTypeNoValue);
    case AResultType^.Kind of
      tkSString, tkAString, tkWString, tkArray, tkInterface, tkDynArray,
        tkUString: VRetInParam := True;
      tkRecord: VRetInParam := GetTypeData(AResultType)^.RecSize > 8;
    end;
  end;
  VStack := nil;
  VHandle := Default(TInvokeHandle);
  VGPRegIdx := 0;
  VFPRegIdx := 0;
  if VRetInParam then
  begin
    if fcfStatic in AFlags then
      VRetReg := 0
    else
      VRetReg := 1;
    VHandle.GPRegs[VRetReg] := PtrUInt(AResultValue);
  end
  else
    VRetReg := -1;
  for I := 0 to High(AArgs) do
  begin
    VArg := AArgs[I];
    if VArg.Info.ParamFlags * [pfArray, pfOut, pfVar, pfConstRef] <> [] then
      PushVal(PtrUInt(VArg.ValueRef))
    else
      if not MapVal(GetTypeData(VArg.Info.ParamType),
        VArg.Info.ParamType^.Kind, VArg.ValueRef) then
        raise EInvocationError.CreateFmt(SErrFailedToConvertArg,
          [I, VArg.Info.ParamType^.Name]);
  end;
  VHandle.StackAddr := @VStack[0];
  VHandle.FuncAddr := AFuncAddr;
  if AResultType = TypeInfo(Extended) then
  begin
    VHandle.ResultType := X87_CLASS;
    VRetSize := 10;
  end
  else
  begin
    VHandle.ResultType := INTEGER_CLASS;
    VRetSize := 8;
  end;
  InvokeKernelUnix64(VHandle);
  if Assigned(AResultType) and not VRetInParam then
    Move(VHandle.Result, PPtrUInt(AResultValue)^, VRetSize);
{$ELSE}
  raise EInvocationError.Create(SErrPlatformNotSupported);
{$ENDIF}
end;

const
  CallManager: TFunctionCallManager = (
    Invoke: @CallManagerInvoke;
    CreateCallbackProc: nil;
    CreateCallbackMethod: nil;
    FreeCallback: nil;
  );

procedure InitSystemFunctionCallManager;
begin
  SetFunctionCallManager([ccReg, ccCdecl, ccPascal, ccStdCall], CallManager);
end;
invoke_unix64.inc (10,172 bytes)

silvioprog

2018-12-21 08:36

reporter  

tests.pp (59,385 bytes)
program tests;

{$MODE DELPHI}
{$ASSERTIONS ON}
{$WARN 4055 OFF}
{$WARN 5024 OFF}
{$WARN 5026 OFF}
{$WARN 5079 OFF}

uses
  SysUtils,
  Math,
  TypInfo,
  Rtti;

{$I invoke_unix64.inc}

type
  TArrayOfLongIntDyn = array of LongInt;

  TArrayOfLongIntStatic = array[0..2] of LongInt;

  TTestEnum = (te1, te2, te3, te4, te5, te6, te7, te8, te9, te10);

  TTestSet = set of TTestEnum;

  TTestProc = procedure;

  TTestFunc1 = function: LongInt;

  TTestFunc2 = function(AArg1: LongInt; AArg2: array of LongInt): string;

  TTestMethod1 = procedure of object;

  TTestMethodObj = class
  public
    procedure TestMethod1;
    procedure TestMethod2;
  end;

  TTestRecord1 = record
    Member1, Member2: LongInt;
  end;

  TTestRecord2 = record
    Member1: string;
    Member2: LongInt;
  end;

  TTestRecord3 = record
    Member1: LongInt;
    Member2: Double;
    Member3: string;
    Member4: Extended;
  end;

  TTestClass1 = class
    Field1, Field2: LongInt;
  end;

  TTestClass2 = class
    Field1: string;
    Field2: LongInt;
  end;

  TTestClass3 = class
    Field1: LongInt;
    Field2: Double;
    Field3: string;
    Field4: Extended;
  end;

  ITestIntf1 = interface
    function GetField1: LongInt;
    function GetField2: LongInt;
    procedure SetField1(AValue: LongInt);
    procedure SetField2(AValue: LongInt);
    property Field1: LongInt read GetField1 write SetField1;
    property Field2: LongInt read GetField2 write SetField2;
  end;

  TTestIntf1 = class(TInterfacedObject, ITestIntf1)
  private
    FField1: LongInt;
    FField2: LongInt;
    function GetField1: LongInt;
    function GetField2: LongInt;
    procedure SetField1(AValue: LongInt);
    procedure SetField2(AValue: LongInt);
  public
    property Field1: LongInt read GetField1 write SetField1;
    property Field2: LongInt read GetField2 write SetField2;
  end;

  ITestIntf2 = interface
    function GetField1: string;
    function GetField2: LongInt;
    procedure SetField1(const AValue: string);
    procedure SetField2(AValue: LongInt);
    property Field1: string read GetField1 write SetField1;
    property Field2: LongInt read GetField2 write SetField2;
  end;

  TTestIntf2 = class(TInterfacedObject, ITestIntf2)
  private
    FField1: string;
    FField2: LongInt;
    function GetField1: string;
    function GetField2: LongInt;
    procedure SetField1(const AValue: string);
    procedure SetField2(AValue: LongInt);
  public
    property Field1: string read GetField1 write SetField1;
    property Field2: LongInt read GetField2 write SetField2;
  end;

  ITestIntf3 = interface
    function GetField1: LongInt;
    function GetField2: Double;
    function GetField3: string;
    function GetField4: Extended;
    procedure SetField1(AValue: LongInt);
    procedure SetField2(AValue: Double);
    procedure SetField3(const AValue: string);
    procedure SetField4(AValue: Extended);
    property Field1: LongInt read GetField1 write SetField1;
    property Field2: Double read GetField2 write SetField2;
    property Field3: string read GetField3 write SetField3;
    property Field4: Extended read GetField4 write SetField4;
  end;

  TTestIntf3 = class(TInterfacedObject, ITestIntf3)
  private
    FField1: LongInt;
    FField2: Double;
    FField3: string;
    FField4: Extended;
    function GetField1: LongInt;
    function GetField2: Double;
    function GetField3: string;
    function GetField4: Extended;
    procedure SetField1(AValue: LongInt);
    procedure SetField2(AValue: Double);
    procedure SetField3(const AValue: string);
    procedure SetField4(AValue: Extended);
  public
    property Field1: LongInt read GetField1 write SetField1;
    property Field2: Double read GetField2 write SetField2;
    property Field3: string read GetField3 write SetField3;
    property Field4: Extended read GetField4 write SetField4;
  end;

var
  GProcOK1: Boolean = False;
  GProcOK2: Boolean = True;
  GVal1: string = 'abc';
  GVal2: Integer = 123;
  GVal3: Double = 12.34;

function EqF32(A, B: Single): Boolean; inline;
begin
  Result := CompareValue(A, B) = 0;
end;

function EqF64(A, B: Double): Boolean; inline;
begin
  Result := CompareValue(A, B) = 0;
end;

function EqF80(A, B: Extended): Boolean; inline;
begin
  Result := CompareValue(A, B) = 0;
end;

function EqArr(A, B: array of LongInt): Boolean;
var
  I: Integer;
begin
  Result := Length(A) = Length(B);
  if Result then
    for I := Low(A) to High(A) do
      if A[I] <> B[I] then
        Exit(False);
  Result := True;
end;

procedure TestProcedure1;
begin
  GProcOK1 := True;
end;

procedure TestProcedure2;
begin
  GProcOK2 := False;
end;

function TestFunction1: LongInt;
begin
  Result := 123;
end;

function TestFunction2: LongInt;
begin
  Result := 456;
end;

function TestFunction3(AArg1: LongInt; AArg2: array of LongInt): string;
var
  I: LongInt;
begin
  Result := AArg1.ToString;
  for I in AArg2 do
    Result += I.ToString;
end;

function TestFunction4(AArg1: LongInt; AArg2: array of LongInt): string;
var
  I: LongInt;
begin
  Result := AArg1.ToString;
  for I in AArg2 do
    Result += I.ToString;
  Result += 'abc123';
end;

{ TTestMethodObj }

procedure TTestMethodObj.TestMethod1;
begin
  GProcOK1 := True;
end;

procedure TTestMethodObj.TestMethod2;
begin
  GProcOK2 := False;
end;

{ TTestIntf1 }

function TTestIntf1.GetField1: LongInt;
begin
  Result := FField1;
end;

function TTestIntf1.GetField2: LongInt;
begin
  Result := FField2;
end;

procedure TTestIntf1.SetField1(AValue: LongInt);
begin
  FField1 := AValue;
end;

procedure TTestIntf1.SetField2(AValue: LongInt);
begin
  FField2 := AValue;
end;

{ TTestIntf2 }

function TTestIntf2.GetField1: string;
begin
  Result := FField1;
end;

function TTestIntf2.GetField2: LongInt;
begin
  Result := FField2;
end;

procedure TTestIntf2.SetField1(const AValue: string);
begin
  FField1 := AValue;
end;

procedure TTestIntf2.SetField2(AValue: LongInt);
begin
  FField2 := AValue;
end;

{ TTestIntf3 }

function TTestIntf3.GetField1: LongInt;
begin
  Result := FField1;
end;

function TTestIntf3.GetField2: Double;
begin
  Result := FField2;
end;

function TTestIntf3.GetField3: string;
begin
  Result := FField3;
end;

function TTestIntf3.GetField4: Extended;
begin
  Result := FField4;
end;

procedure TTestIntf3.SetField1(AValue: LongInt);
begin
  FField1 := AValue;
end;

procedure TTestIntf3.SetField2(AValue: Double);
begin
  FField2 := AValue;
end;

procedure TTestIntf3.SetField3(const AValue: string);
begin
  FField3 := AValue;
end;

procedure TTestIntf3.SetField4(AValue: Extended);
begin
  FField4 := AValue;
end;

{ B8 }

function TestB8(P1, P2: Boolean): Boolean;
begin
  Assert((not P1) and P2);
  Result := False;
end;

function TestManyB8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Boolean): Boolean;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := True;
end;

{ B16 }

function TestB16(P1, P2: Boolean16): Boolean16;
begin
  Assert((not P1) and P2);
  Result := Boolean16(0);
end;

function TestManyB16(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: Boolean16): Boolean16;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := Boolean16(1);
end;

{ B32 }

function TestB32(P1, P2: Boolean32): Boolean32;
begin
  Assert((not P1) and P2);
  Result := Boolean32(0);
end;

function TestManyB32(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: Boolean32): Boolean32;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := Boolean32(1);
end;

{ B64 }

function TestB64(P1, P2: Boolean64): Boolean64;
begin
  Assert((not P1) and P2);
  Result := Boolean64(0);
end;

function TestManyB64(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: Boolean64): Boolean64;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := Boolean64(1);
end;

{ BL8 }

function TestBL8(P1, P2: ByteBool): ByteBool;
begin
  Assert((not P1) and P2);
  Result := ByteBool(0);
end;

function TestManyBL8(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: ByteBool): ByteBool;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := ByteBool(1);
end;

{ BL16 }

function TestBL16(P1, P2: WordBool): WordBool;
begin
  Assert((not P1) and P2);
  Result := WordBool(0);
end;

function TestManyBL16(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: WordBool): WordBool;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := WordBool(1);
end;

{ BL32 }

function TestBL32(P1, P2: LongBool): LongBool;
begin
  Assert((not P1) and P2);
  Result := LongBool(0);
end;

function TestManyBL32(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: LongBool): LongBool;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := LongBool(1);
end;

{ BL64 }

function TestBL64(P1, P2: QWordBool): QWordBool;
begin
  Assert((not P1) and P2);
  Result := QWordBool(0);
end;

function TestManyBL64(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: QWordBool): QWordBool;
begin
  Assert((not P1) and P2 and (not P3) and P4 and (not P5) and P6 and
    (not P7) and P8 and (not P9) and P10);
  Result := QWordBool(1);
end;

{ U8 }

function TestU8(P1, P2: UInt8): UInt8;
begin
  Assert((P1 = Low(UInt8)) and (P2 = High(UInt8)));
  Result := High(UInt8);
end;

function TestManyU8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt8): UInt8;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(UInt8)) and (P10 = High(UInt8)));
  Result := High(UInt8);
end;

{ S8 }

function TestS8(P1, P2: Int8): Int8;
begin
  Assert((P1 = Low(Int8)) and (P2 = High(Int8)));
  Result := High(Int8);
end;

function TestManyS8(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int8): Int8;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(Int8)) and (P10 = High(Int8)));
  Result := High(Int8);
end;

{ U16 }

function TestU16(P1, P2: UInt16): UInt16;
begin
  Assert((P1 = Low(UInt16)) and (P2 = High(UInt16)));
  Result := High(UInt16);
end;

function TestManyU16(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt16): UInt16;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(UInt16)) and (P10 = High(UInt16)));
  Result := High(UInt16);
end;

{ S16 }

function TestS16(P1, P2: Int16): Int16;
begin
  Assert((P1 = Low(Int16)) and (P2 = High(Int16)));
  Result := High(Int16);
end;

function TestManyS16(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int16): Int16;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(Int16)) and (P10 = High(Int16)));
  Result := High(Int16);
end;

{ U32 }

function TestU32(P1, P2: UInt32): UInt32;
begin
  Assert((P1 = Low(UInt32)) and (P2 = High(UInt32)));
  Result := High(UInt32);
end;

function TestManyU32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt32): UInt32;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(UInt32)) and (P10 = High(UInt32)));
  Result := High(UInt32);
end;

{ S32 }

function TestS32(P1, P2: Int32): Int32;
begin
  Assert((P1 = Low(Int32)) and (P2 = High(Int32)));
  Result := High(Int32);
end;

function TestManyS32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int32): Int32;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(Int32)) and (P10 = High(Int32)));
  Result := High(Int32);
end;

{ U64 }

function TestU64(P1, P2: UInt64): UInt64;
begin
  Assert((P1 = Low(UInt64)) and (P2 = High(UInt64)));
  Result := High(UInt64);
end;

function TestManyU64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: UInt64): UInt64;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(UInt64)) and (P10 = High(UInt64)));
  Result := High(UInt64);
end;

{ S64 }

function TestS64(P1, P2: Int64): Int64;
begin
  Assert((P1 = Low(Int64)) and (P2 = High(Int64)));
  Result := High(Int64);
end;

function TestManyS64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Int64): Int64;
begin
  Assert((P1 = 11) and (P2 = 22) and (P3 = 33) and (P4 = 44) and
    (P5 = 55) and (P6 = 66) and (P7 = 77) and (P8 = 88) and
    (P9 = Low(Int64)) and (P10 = High(Int64)));
  Result := High(Int64);
end;

{ F32 }

function TestF32(P1, P2: Single): Single;
begin
  Assert(EqF32(P1, MinSingle) and EqF32(P2, MaxSingle));
  Result := MaxSingle;
end;

function TestManyF32(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Single): Single;
begin
  Assert(EqF32(P1, 11.11) and EqF32(P2, 22.22) and EqF32(P3, 33.33) and
    EqF32(P4, 44.44) and EqF32(P5, 55.55) and EqF32(P6, 66.66) and
    EqF32(P7, 77.77) and EqF32(P8, 88.88) and EqF32(P9, MinSingle) and
    EqF32(P10, MaxSingle));
  Result := MaxSingle;
end;

{ F64 }

function TestF64(P1, P2: Double): Double;
begin
  Assert(EqF64(P1, MinDouble) and EqF64(P2, MaxDouble));
  Result := MaxDouble;
end;

function TestManyF64(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Double): Double;
begin
  Assert(EqF64(P1, 11.11) and EqF64(P2, 22.22) and EqF64(P3, 33.33) and
    EqF64(P4, 44.44) and EqF64(P5, 55.55) and EqF64(P6, 66.66) and
    EqF64(P7, 77.77) and EqF64(P8, 88.88) and EqF64(P9, MinDouble) and
    EqF64(P10, MaxDouble));
  Result := MaxDouble;
end;

{ FCU }

function TestFCU(P1, P2: Currency): Currency;
begin
  Assert(EqF64(P1, MinCurrency) and EqF64(P2, MaxCurrency));
  Result := MaxCurrency;
end;

function TestManyFCU(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: Currency): Currency;
begin
  Assert(EqF64(P1, 11.11) and EqF64(P2, 22.22) and EqF64(P3, 33.33) and
    EqF64(P4, 44.44) and EqF64(P5, 55.55) and EqF64(P6, 66.66) and
    EqF64(P7, 77.77) and EqF64(P8, 88.88) and EqF64(P9, MinCurrency) and
    EqF64(P10, MaxCurrency));
  Result := MaxCurrency;
end;

{ FCO }

function TestFCO(P1, P2: Comp): Comp;
begin
  Assert((P1 = 123) and (P2 = 456));
  Result := 789;
end;

{ F80 }

function TestF80(P1, P2: Extended): Extended;
begin
  Assert(EqF80(P1, MinExtended) and EqF80(P2, MaxExtended));
  Result := MaxExtended;
end;

function TestManyF80(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: Extended): Extended;
begin
  Assert(EqF80(P1, 11.11) and EqF80(P2, 22.22) and EqF80(P3, 33.33) and
    EqF80(P4, 44.44) and EqF80(P5, 55.55) and EqF80(P6, 66.66) and
    EqF80(P7, 77.77) and EqF80(P8, 88.88) and EqF80(P9, MinExtended) and
    EqF80(P10, MaxExtended));
  Result := MaxExtended;
end;

{ SS }

function TestSS(P1, P2: ShortString): ShortString;
begin
  Assert((P1 = 'abc') and (P2 = '123'));
  Result := 'abc123';
end;

function TestManySS(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: ShortString): ShortString;
begin
  Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
    (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
    (P9 = 'S8') and (P10 = 'S9'));
  Result := 'abc123';
end;

{ SA }

function TestSA(P1, P2: AnsiString): AnsiString;
begin
  Assert((P1 = 'abc') and (P2 = '123'));
  Result := 'abc123';
end;

function TestManySA(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: AnsiString): AnsiString;
begin
  Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
    (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
    (P9 = 'S8') and (P10 = 'S9'));
  Result := 'abc123';
end;

{ SU }

function TestSU(P1, P2: UnicodeString): UnicodeString;
begin
  Assert((P1 = 'abc') and (P2 = '123'));
  Result := 'abc123';
end;

function TestManySU(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: UnicodeString): UnicodeString;
begin
  Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
    (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
    (P9 = 'S8') and (P10 = 'S9'));
  Result := 'abc123';
end;

{ SW }

function TestSW(P1, P2: WideString): WideString;
begin
  Assert((P1 = 'abc') and (P2 = '123'));
  Result := 'abc123';
end;

function TestManySW(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: WideString): WideString;
begin
  Assert((P1 = 'S0') and (P2 = 'S1') and (P3 = 'S2') and (P4 = 'S3') and
    (P5 = 'S4') and (P6 = 'S5') and (P7 = 'S6') and (P8 = 'S7') and
    (P9 = 'S8') and (P10 = 'S9'));
  Result := 'abc123';
end;

{ P }

function TestP(P1, P2: Pointer): Pointer;
begin
  Assert((P1 = @GVal1) and (P2 = @GVal2));
  Result := @GVal3;
end;

function TestManyP(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: Pointer): Pointer;
begin
  Assert((P1 = @GVal1) and (P2 = @GVal2) and (P3 = @GVal3) and (P4 = @GVal1) and
    (P5 = @GVal2) and (P6 = @GVal3) and (P7 = @GVal1) and (P8 = @GVal2) and
    (P9 = @GVal3) and (P10 = @GVal1));
  Result := @GVal3;
end;

{ AD }

function TestAD(P1, P2: TArrayOfLongIntDyn): TArrayOfLongIntDyn;
begin
  Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]));
  Result := [147, 258, 369];
end;

function TestManyAD(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TArrayOfLongIntDyn): TArrayOfLongIntDyn;
begin
  Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]) and
    EqArr(P3, [123, 456]) and EqArr(P4, [789, 654, 321]) and
    EqArr(P5, [123, 456]) and EqArr(P6, [789, 654, 321]) and
    EqArr(P7, [123, 456]) and EqArr(P8, [789, 654, 321]) and
    EqArr(P9, [123, 456]) and EqArr(P10, [789, 654, 321]));
  Result := [963, 852, 741];
end;

{ AS }

function TestAS(P1, P2: TArrayOfLongIntStatic): TArrayOfLongIntStatic;
begin
  Assert(EqArr(P1, [123, 456, 0]) and EqArr(P2, [789, 654, 321]));
  Result[0] := 147;
  Result[1] := 258;
  Result[2] := 369;
end;

function TestManyAS(const P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TArrayOfLongIntStatic): TArrayOfLongIntStatic;
begin
  Assert(EqArr(P1, [123, 456]) and EqArr(P2, [789, 654, 321]) and
    EqArr(P3, [123, 456]) and EqArr(P4, [789, 654, 321]) and
    EqArr(P5, [123, 456]) and EqArr(P6, [789, 654, 321]) and
    EqArr(P7, [123, 456]) and EqArr(P8, [789, 654, 321]) and
    EqArr(P9, [123, 456]) and EqArr(P10, [789, 654, 321]));
  Result[0] := 963;
  Result[1] := 852;
  Result[2] := 741;
end;

{ E }

function TestE(P1, P2: TTestEnum): TTestEnum;
begin
  Assert((P1 = Low(TTestEnum)) and (P2 = High(TTestEnum)));
  Result := High(TTestEnum);
end;

function TestManyE(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestEnum): TTestEnum;
begin
  Assert((P1 = te1) and (P2 = te10) and (P3 = te1) and (P4 = te10) and
    (P5 = te1) and (P6 = te10) and (P7 = te1) and (P8 = te10) and
    (P9 = te1) and (P10 = te10));
  Result := High(TTestEnum);
end;

{ S }

function TestS(P1, P2: TTestSet): TTestSet;
begin
  Assert((te1 in P1) and (te10 in P2));
  Result := [te1, te2, te3];
end;

function TestManyS(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10: TTestSet): TTestSet;
begin
  Assert((P1 = [te1]) and (P2 = [te10]) and (P3 = [te1]) and (P4 = [te10]) and
    (P5 = [te1]) and (P6 = [te10]) and (P7 = [te1]) and (P8 = [te10]) and
    (P9 = [te1]) and (P10 = [te10]));
  Result := [te8, te9, te10];
end;

{ Proc }

function TestProc(P1, P2: TTestProc): TTestProc;
begin
  Assert((@P1 = @TestProcedure1) and (@P2 = @TestProcedure2));
  P1;
  P2;
  Result := TestProcedure1;
end;

function TestManyProc(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestProc): TTestProc;
begin
  Assert((@P1 = @TestProcedure1) and (@P2 = @TestProcedure2) and
    (@P3 = @TestProcedure1) and (@P4 = @TestProcedure2) and
    (@P5 = @TestProcedure1) and (@P6 = @TestProcedure2) and
    (@P7 = @TestProcedure1) and (@P8 = @TestProcedure2) and
    (@P9 = @TestProcedure1) and (@P10 = @TestProcedure2));
  P1;
  P10;
  Result := TestProcedure1;
end;

{ Func 1 }

function TestFunc1(P1, P2: TTestFunc1): TTestFunc1;
begin
  Assert((@P1 = @TestFunction1) and (@P2 = @TestFunction2));
  Assert(P1 = 123);
  Assert(P2 = 456);
  Result := TestFunction1;
end;

function TestManyFunc1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestFunc1): TTestFunc1;
begin
  Assert((@P1 = @TestFunction1) and (@P2 = @TestFunction2) and
    (@P3 = @TestFunction1) and (@P4 = @TestFunction2) and
    (@P5 = @TestFunction1) and (@P6 = @TestFunction2) and
    (@P7 = @TestFunction1) and (@P8 = @TestFunction2) and
    (@P9 = @TestFunction1) and (@P10 = @TestFunction2));
  Assert(P1 = 123);
  Assert(P2 = 456);
  Assert(P3 = 123);
  Assert(P4 = 456);
  Assert(P5 = 123);
  Assert(P6 = 456);
  Assert(P7 = 123);
  Assert(P8 = 456);
  Assert(P9 = 123);
  Assert(P10 = 456);
  Result := TestFunction2;
end;

{ Func 2 }

function TestFunc2(P1, P2: TTestFunc2): TTestFunc2;
begin
  Assert((@P1 = @TestFunction3) and (@P2 = @TestFunction4));
  Assert(P1(123, [456, 789]) = '123456789');
  Assert(P2(456, [789, 456, 123]) = '456789456123abc123');
  Result := TestFunction3;
end;

function TestManyFunc2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestFunc2): TTestFunc2;
begin
  Assert((@P1 = @TestFunction3) and (@P2 = @TestFunction4) and
    (@P3 = @TestFunction3) and (@P4 = @TestFunction4) and
    (@P5 = @TestFunction3) and (@P6 = @TestFunction4) and
    (@P7 = @TestFunction3) and (@P8 = @TestFunction4) and
    (@P9 = @TestFunction3) and (@P10 = @TestFunction4));
  Assert(P1(123, [456, 789]) = '123456789');
  Assert(P10(456, [789, 456, 123]) = '456789456123abc123');
  Result := TestFunction4;
end;

{ Method }

function TestMethod1(S: TTestMethodObj; P1, P2: TTestMethod1): TTestMethod1;
begin
  Assert((@P1 = @TTestMethodObj.TestMethod1) and
    (@P2 = @TTestMethodObj.TestMethod2));
  P1;
  P2;
  Result := S.TestMethod1;
end;

function TestManyMethod1(S: TTestMethodObj; P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestMethod1): TTestMethod1;
begin
  Assert((@P1 = @TTestMethodObj.TestMethod1) and
    (@P2 = @TTestMethodObj.TestMethod2) and
    (@P3 = @TTestMethodObj.TestMethod1) and
    (@P4 = @TTestMethodObj.TestMethod2) and
    (@P5 = @TTestMethodObj.TestMethod1) and
    (@P6 = @TTestMethodObj.TestMethod2) and
    (@P7 = @TTestMethodObj.TestMethod1) and
    (@P8 = @TTestMethodObj.TestMethod2) and
    (@P9 = @TTestMethodObj.TestMethod1) and
    (@P10 = @TTestMethodObj.TestMethod2));
  P1;
  P10;
  Result := S.TestMethod1;
end;

{ R1 }

function TestRecord1(P1, P2: TTestRecord1): TTestRecord1;
begin
  Assert((P1.Member1 = 123) and (P1.Member2 = 456) and (P2.Member1 = 987) and
    (P2.Member2 = 654));
  Result.Member1 := 147;
  Result.Member2 := 258;
end;

function TestManyRecord1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestRecord1): TTestRecord1;
begin
  Assert((P1.Member1 = 123) and (P1.Member2 = 456) and (P2.Member1 = 987) and
    (P2.Member2 = 654) and (P3.Member1 = 123) and (P3.Member2 = 456) and
    (P4.Member1 = 987) and (P4.Member2 = 654) and (P5.Member1 = 123) and
    (P5.Member2 = 456) and (P6.Member1 = 987) and (P6.Member2 = 654) and
    (P7.Member1 = 123) and (P7.Member2 = 456) and (P8.Member1 = 987) and
    (P8.Member2 = 654) and (P9.Member1 = 123) and (P9.Member2 = 456) and
    (P10.Member1 = 987) and (P10.Member2 = 654));
  Result.Member1 := 258;
  Result.Member2 := 147;
end;

{ R2 }

function TestRecord2(P1, P2: TTestRecord2): TTestRecord2;
begin
  Assert((P1.Member1 = 'abc') and (P1.Member2 = 123) and
    (P2.Member1 = 'def') and (P2.Member2 = 456));
  Result.Member1 := 'ghi';
  Result.Member2 := 789;
end;

function TestManyRecord2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestRecord2): TTestRecord2;
begin
  Assert((P1.Member1 = 'abc') and (P1.Member2 = 123) and
    (P2.Member1 = 'def') and (P2.Member2 = 456) and (P3.Member1 = 'abc') and
    (P3.Member2 = 123) and (P4.Member1 = 'def') and (P4.Member2 = 456) and
    (P5.Member1 = 'abc') and (P5.Member2 = 123) and (P6.Member1 = 'def') and
    (P6.Member2 = 456) and (P7.Member1 = 'abc') and (P7.Member2 = 123) and
    (P8.Member1 = 'def') and (P8.Member2 = 456) and (P9.Member1 = 'abc') and
    (P9.Member2 = 123) and (P10.Member1 = 'def') and (P10.Member2 = 456));
  Result.Member1 := 'xyz';
  Result.Member2 := 951;
end;

{ R3 }

function TestRecord3(P1, P2: TTestRecord3): TTestRecord3;
begin
  Assert((P1.Member1 = 11) and EqF64(P1.Member2, 22.22) and
    (P1.Member3 = 'aaa') and EqF80(P1.Member4, 33.33) and
    (P2.Member1 = 22) and EqF64(P2.Member2, 44.44) and
    (P2.Member3 = 'bbb') and EqF80(P2.Member4, 55.55));
  Result.Member1 := 123;
  Result.Member2 := 12.34;
  Result.Member3 := 'abc';
  Result.Member4 := 45.67;
end;

function TestManyRecord3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestRecord3): TTestRecord3;
begin
  Assert((P1.Member1 = 11) and EqF64(P1.Member2, 22.22) and
    (P1.Member3 = 'aaa') and EqF80(P1.Member4, 33.33) and
    (P2.Member1 = 22) and EqF64(P2.Member2, 44.44) and
    (P2.Member3 = 'bbb') and EqF80(P2.Member4, 55.55) and
    (P3.Member1 = 11) and EqF64(P3.Member2, 22.22) and
    (P3.Member3 = 'aaa') and EqF80(P3.Member4, 33.33) and
    (P4.Member1 = 22) and EqF64(P4.Member2, 44.44) and
    (P4.Member3 = 'bbb') and EqF80(P4.Member4, 55.55) and
    (P5.Member1 = 11) and EqF64(P5.Member2, 22.22) and
    (P5.Member3 = 'aaa') and EqF80(P5.Member4, 33.33) and
    (P6.Member1 = 22) and EqF64(P6.Member2, 44.44) and
    (P6.Member3 = 'bbb') and EqF80(P6.Member4, 55.55) and
    (P7.Member1 = 11) and EqF64(P7.Member2, 22.22) and
    (P7.Member3 = 'aaa') and EqF80(P7.Member4, 33.33) and
    (P8.Member1 = 22) and EqF64(P8.Member2, 44.44) and
    (P8.Member3 = 'bbb') and EqF80(P8.Member4, 55.55) and
    (P9.Member1 = 11) and EqF64(P9.Member2, 22.22) and
    (P9.Member3 = 'aaa') and EqF80(P9.Member4, 33.33) and
    (P10.Member1 = 22) and EqF64(P10.Member2, 44.44) and
    (P10.Member3 = 'bbb') and EqF80(P10.Member4, 55.55));
  Result.Member1 := 321;
  Result.Member2 := 43.21;
  Result.Member3 := 'def';
  Result.Member4 := 76.54;
end;

{ C1 }

function TestClass1(P1, P2: TTestClass1): TTestClass1;
begin
  Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
    (P2.Field2 = 654));
  Result := TTestClass1.Create;
  Result.Field1 := 147;
  Result.Field2 := 258;
end;

function TestManyClass1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestClass1): TTestClass1;
begin
  Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
    (P2.Field2 = 654) and (P3.Field1 = 123) and (P3.Field2 = 456) and
    (P4.Field1 = 987) and (P4.Field2 = 654) and (P5.Field1 = 123) and
    (P5.Field2 = 456) and (P6.Field1 = 987) and (P6.Field2 = 654) and
    (P7.Field1 = 123) and (P7.Field2 = 456) and (P8.Field1 = 987) and
    (P8.Field2 = 654) and (P9.Field1 = 123) and (P9.Field2 = 456) and
    (P10.Field1 = 987) and (P10.Field2 = 654));
  Result := TTestClass1.Create;
  Result.Field1 := 258;
  Result.Field2 := 147;
end;

{ C2 }

function TestClass2(P1, P2: TTestClass2): TTestClass2;
begin
  Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
    (P2.Field1 = 'def') and (P2.Field2 = 456));
  Result := TTestClass2.Create;
  Result.Field1 := 'ghi';
  Result.Field2 := 789;
end;

function TestManyClass2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestClass2): TTestClass2;
begin
  Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
    (P2.Field1 = 'def') and (P2.Field2 = 456) and (P3.Field1 = 'abc') and
    (P3.Field2 = 123) and (P4.Field1 = 'def') and (P4.Field2 = 456) and
    (P5.Field1 = 'abc') and (P5.Field2 = 123) and (P6.Field1 = 'def') and
    (P6.Field2 = 456) and (P7.Field1 = 'abc') and (P7.Field2 = 123) and
    (P8.Field1 = 'def') and (P8.Field2 = 456) and (P9.Field1 = 'abc') and
    (P9.Field2 = 123) and (P10.Field1 = 'def') and (P10.Field2 = 456));
  Result := TTestClass2.Create;
  Result.Field1 := 'xyz';
  Result.Field2 := 951;
end;

{ C3 }

function TestClass3(P1, P2: TTestClass3): TTestClass3;
begin
  Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
    (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
    (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
    (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55));
  Result := TTestClass3.Create;
  Result.Field1 := 123;
  Result.Field2 := 12.34;
  Result.Field3 := 'abc';
  Result.Field4 := 45.67;
end;

function TestManyClass3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: TTestClass3): TTestClass3;
begin
  Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
    (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
    (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
    (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55) and
    (P3.Field1 = 11) and EqF64(P3.Field2, 22.22) and
    (P3.Field3 = 'aaa') and EqF80(P3.Field4, 33.33) and
    (P4.Field1 = 22) and EqF64(P4.Field2, 44.44) and
    (P4.Field3 = 'bbb') and EqF80(P4.Field4, 55.55) and
    (P5.Field1 = 11) and EqF64(P5.Field2, 22.22) and
    (P5.Field3 = 'aaa') and EqF80(P5.Field4, 33.33) and
    (P6.Field1 = 22) and EqF64(P6.Field2, 44.44) and
    (P6.Field3 = 'bbb') and EqF80(P6.Field4, 55.55) and
    (P7.Field1 = 11) and EqF64(P7.Field2, 22.22) and
    (P7.Field3 = 'aaa') and EqF80(P7.Field4, 33.33) and
    (P8.Field1 = 22) and EqF64(P8.Field2, 44.44) and
    (P8.Field3 = 'bbb') and EqF80(P8.Field4, 55.55) and
    (P9.Field1 = 11) and EqF64(P9.Field2, 22.22) and
    (P9.Field3 = 'aaa') and EqF80(P9.Field4, 33.33) and
    (P10.Field1 = 22) and EqF64(P10.Field2, 44.44) and
    (P10.Field3 = 'bbb') and EqF80(P10.Field4, 55.55));
  Result := TTestClass3.Create;
  Result.Field1 := 321;
  Result.Field2 := 43.21;
  Result.Field3 := 'def';
  Result.Field4 := 76.54;
end;

{ I1 }

function TestIntf1(P1, P2: ITestIntf1): ITestIntf1;
begin
  Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
    (P2.Field2 = 654));
  Result := TTestIntf1.Create;
  Result.Field1 := 147;
  Result.Field2 := 258;
end;

function TestManyIntf1(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: ITestIntf1): ITestIntf1;
begin
  Assert((P1.Field1 = 123) and (P1.Field2 = 456) and (P2.Field1 = 987) and
    (P2.Field2 = 654) and (P3.Field1 = 123) and (P3.Field2 = 456) and
    (P4.Field1 = 987) and (P4.Field2 = 654) and (P5.Field1 = 123) and
    (P5.Field2 = 456) and (P6.Field1 = 987) and (P6.Field2 = 654) and
    (P7.Field1 = 123) and (P7.Field2 = 456) and (P8.Field1 = 987) and
    (P8.Field2 = 654) and (P9.Field1 = 123) and (P9.Field2 = 456) and
    (P10.Field1 = 987) and (P10.Field2 = 654));
  Result := TTestIntf1.Create;
  Result.Field1 := 258;
  Result.Field2 := 147;
end;

{ I2 }

function TestIntf2(P1, P2: ITestIntf2): ITestIntf2;
begin
  Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
    (P2.Field1 = 'def') and (P2.Field2 = 456));
  Result := TTestIntf2.Create;
  Result.Field1 := 'ghi';
  Result.Field2 := 789;
end;

function TestManyIntf2(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: ITestIntf2): ITestIntf2;
begin
  Assert((P1.Field1 = 'abc') and (P1.Field2 = 123) and
    (P2.Field1 = 'def') and (P2.Field2 = 456) and (P3.Field1 = 'abc') and
    (P3.Field2 = 123) and (P4.Field1 = 'def') and (P4.Field2 = 456) and
    (P5.Field1 = 'abc') and (P5.Field2 = 123) and (P6.Field1 = 'def') and
    (P6.Field2 = 456) and (P7.Field1 = 'abc') and (P7.Field2 = 123) and
    (P8.Field1 = 'def') and (P8.Field2 = 456) and (P9.Field1 = 'abc') and
    (P9.Field2 = 123) and (P10.Field1 = 'def') and (P10.Field2 = 456));
  Result := TTestIntf2.Create;
  Result.Field1 := 'xyz';
  Result.Field2 := 951;
end;

{ I3 }

function TestIntf3(P1, P2: ITestIntf3): ITestIntf3;
begin
  Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
    (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
    (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
    (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55));
  Result := TTestIntf3.Create;
  Result.Field1 := 123;
  Result.Field2 := 12.34;
  Result.Field3 := 'abc';
  Result.Field4 := 45.67;
end;

function TestManyIntf3(P1, P2, P3, P4, P5, P6, P7, P8, P9,
  P10: ITestIntf3): ITestIntf3;
begin
  Assert((P1.Field1 = 11) and EqF64(P1.Field2, 22.22) and
    (P1.Field3 = 'aaa') and EqF80(P1.Field4, 33.33) and
    (P2.Field1 = 22) and EqF64(P2.Field2, 44.44) and
    (P2.Field3 = 'bbb') and EqF80(P2.Field4, 55.55) and
    (P3.Field1 = 11) and EqF64(P3.Field2, 22.22) and
    (P3.Field3 = 'aaa') and EqF80(P3.Field4, 33.33) and
    (P4.Field1 = 22) and EqF64(P4.Field2, 44.44) and
    (P4.Field3 = 'bbb') and EqF80(P4.Field4, 55.55) and
    (P5.Field1 = 11) and EqF64(P5.Field2, 22.22) and
    (P5.Field3 = 'aaa') and EqF80(P5.Field4, 33.33) and
    (P6.Field1 = 22) and EqF64(P6.Field2, 44.44) and
    (P6.Field3 = 'bbb') and EqF80(P6.Field4, 55.55) and
    (P7.Field1 = 11) and EqF64(P7.Field2, 22.22) and
    (P7.Field3 = 'aaa') and EqF80(P7.Field4, 33.33) and
    (P8.Field1 = 22) and EqF64(P8.Field2, 44.44) and
    (P8.Field3 = 'bbb') and EqF80(P8.Field4, 55.55) and
    (P9.Field1 = 11) and EqF64(P9.Field2, 22.22) and
    (P9.Field3 = 'aaa') and EqF80(P9.Field4, 33.33) and
    (P10.Field1 = 22) and EqF64(P10.Field2, 44.44) and
    (P10.Field3 = 'bbb') and EqF80(P10.Field4, 55.55));
  Result := TTestIntf3.Create;
  Result.Field1 := 321;
  Result.Field2 := 43.21;
  Result.Field3 := 'def';
  Result.Field4 := 76.54;
end;

var
  I: Integer;
  V1, V2: TValue;
  VA: array of TValue = nil;
  B8: Boolean;
  B16: Boolean16;
  B32: Boolean32;
  B64: Boolean64;
  BL8: ByteBool;
  BL16: WordBool;
  BL32: LongBool;
  BL64: QWordBool;
  U8: UInt8;
  S8: Int8;
  U16: UInt16;
  S16: Int16;
  U32: UInt32;
  S32: Int32;
  U64: UInt64;
  S64: Int64;
  F32: Single;
  F64: Double;
  FCU: Currency;
  FCO: Comp;
  F80: Extended;
  SS: ShortString;
  SA: AnsiString;
  SU: UnicodeString;
  SW: WideString;
  P: Pointer;
  AD: TArrayOfLongIntDyn;
  AS_: TArrayOfLongIntStatic;
  E: TTestEnum;
  S: TTestSet;
  Proc: TTestProc;
  Func1: TTestFunc1;
  Func2: TTestFunc2;
  Method1: TTestMethod1;
  TestMethodObj: TTestMethodObj;
  R1: TTestRecord1;
  R2: TTestRecord2;
  R3: TTestRecord3;
  C1, C12, C1R: TTestClass1;
  C2, C22, C2R: TTestClass2;
  C3, C32, C3R: TTestClass3;
  I1, I12, I1R: ITestIntf1;
  I2, I22, I2R: ITestIntf2;
  I3, I32, I3R: ITestIntf3;
begin
  InitSystemFunctionCallManager;

  Rtti.Invoke(@TestProcedure1, nil, ccReg, nil, True, False);
  Assert(GProcOK1);

  { B8 }

  B8 := False;
  TValue.Make(@B8, TypeInfo(Boolean), V1);
  B8 := True;
  TValue.Make(@B8, TypeInfo(Boolean), V2);
  Assert(not Rtti.Invoke(@TestB8, [V1, V2], ccReg, TypeInfo(Boolean),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyB8, [Boolean(False), Boolean(True),
    Boolean(False), Boolean(True), Boolean(False), Boolean(True),
    Boolean(False), Boolean(True), Boolean(False), Boolean(True)],
    ccReg, TypeInfo(Boolean), True, False).AsBoolean);

  { B16 }

  B16 := False;
  TValue.Make(@B16, TypeInfo(Boolean16), V1);
  B16 := True;
  TValue.Make(@B16, TypeInfo(Boolean16), V2);
  Assert(not Rtti.Invoke(@TestB16, [V1, V2], ccReg, TypeInfo(Boolean16),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyB16, [Boolean16(False), Boolean16(True),
    Boolean16(False), Boolean16(True), Boolean16(False), Boolean16(True),
    Boolean16(False), Boolean16(True), Boolean16(False), Boolean16(True)],
    ccReg, TypeInfo(Boolean16), True, False).AsBoolean);

  { B32 }

  B32 := False;
  TValue.Make(@B32, TypeInfo(Boolean32), V1);
  B32 := True;
  TValue.Make(@B32, TypeInfo(Boolean32), V2);
  Assert(not Rtti.Invoke(@TestB32, [V1, V2], ccReg, TypeInfo(Boolean32),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyB32, [Boolean32(False), Boolean32(True),
    Boolean32(False), Boolean32(True), Boolean32(False), Boolean32(True),
    Boolean32(False), Boolean32(True), Boolean32(False), Boolean32(True)],
    ccReg, TypeInfo(Boolean32), True, False).AsBoolean);

  { B64 }

  B64 := False;
  TValue.Make(@B64, TypeInfo(Boolean64), V1);
  B64 := True;
  TValue.Make(@B64, TypeInfo(Boolean64), V2);
  Assert(not Rtti.Invoke(@TestB64, [V1, V2], ccReg, TypeInfo(Boolean64),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyB64, [Boolean64(False), Boolean64(True),
    Boolean64(False), Boolean64(True), Boolean64(False), Boolean64(True),
    Boolean64(False), Boolean64(True), Boolean64(False), Boolean64(True)],
    ccReg, TypeInfo(Boolean64), True, False).AsBoolean);

  { BL8 }

  BL8 := False;
  TValue.Make(@BL8, TypeInfo(ByteBool), V1);
  BL8 := True;
  TValue.Make(@BL8, TypeInfo(ByteBool), V2);
  Assert(not Rtti.Invoke(@TestBL8, [V1, V2], ccReg, TypeInfo(ByteBool),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyBL8, [ByteBool(False), ByteBool(True),
    ByteBool(False), ByteBool(True), ByteBool(False), ByteBool(True),
    ByteBool(False), ByteBool(True), ByteBool(False), ByteBool(True)],
    ccReg, TypeInfo(ByteBool), True, False).AsBoolean);

  { BL16 }

  BL16 := False;
  TValue.Make(@BL16, TypeInfo(WordBool), V1);
  BL16 := True;
  TValue.Make(@BL16, TypeInfo(WordBool), V2);
  Assert(not Rtti.Invoke(@TestBL16, [V1, V2], ccReg, TypeInfo(WordBool),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyBL16, [WordBool(False), WordBool(True),
    WordBool(False), WordBool(True), WordBool(False), WordBool(True),
    WordBool(False), WordBool(True), WordBool(False), WordBool(True)],
    ccReg, TypeInfo(WordBool), True, False).AsBoolean);

  { BL32 }

  BL32 := False;
  TValue.Make(@BL32, TypeInfo(LongBool), V1);
  BL32 := True;
  TValue.Make(@BL32, TypeInfo(LongBool), V2);
  Assert(not Rtti.Invoke(@TestBL32, [V1, V2], ccReg, TypeInfo(LongBool),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyBL32, [LongBool(False), LongBool(True),
    LongBool(False), LongBool(True), LongBool(False), LongBool(True),
    LongBool(False), LongBool(True), LongBool(False), LongBool(True)],
    ccReg, TypeInfo(LongBool), True, False).AsBoolean);

  { BL64 }

  BL64 := False;
  TValue.Make(@BL64, TypeInfo(QWordBool), V1);
  BL64 := True;
  TValue.Make(@BL64, TypeInfo(QWordBool), V2);
  Assert(not Rtti.Invoke(@TestBL64, [V1, V2], ccReg, TypeInfo(QWordBool),
    True, False).AsBoolean);
  Assert(Rtti.Invoke(@TestManyBL64, [QWordBool(False), QWordBool(True),
    QWordBool(False), QWordBool(True), QWordBool(False), QWordBool(True),
    QWordBool(False), QWordBool(True), QWordBool(False), QWordBool(True)],
    ccReg, TypeInfo(QWordBool), True, False).AsBoolean);

  { U8 }

  U8 := Low(UInt8);
  TValue.Make(@U8, TypeInfo(UInt8), V1);
  U8 := High(UInt8);
  TValue.Make(@U8, TypeInfo(UInt8), V2);
  Assert(Rtti.Invoke(@TestU8, [V1, V2], ccReg, TypeInfo(UInt8),
    True, False).AsInteger = High(UInt8));
  Assert(Rtti.Invoke(@TestManyU8, [UInt8(11), UInt8(22), UInt8(33), UInt8(44),
    UInt8(55), UInt8(66), UInt8(77), UInt8(88), UInt8(Low(UInt8)),
    UInt8(High(UInt8))], ccReg, TypeInfo(UInt8),
    True, False).AsInteger = High(UInt8));

  { S8 }

  S8 := Low(Int8);
  TValue.Make(@S8, TypeInfo(Int8), V1);
  S8 := High(Int8);
  TValue.Make(@S8, TypeInfo(Int8), V2);
  Assert(Rtti.Invoke(@TestS8, [V1, V2], ccReg, TypeInfo(Int8),
    True, False).AsInteger = High(Int8));
  Assert(Rtti.Invoke(@TestManyS8, [Int8(11), Int8(22), Int8(33), Int8(44),
    Int8(55), Int8(66), Int8(77), Int8(88), Int8(Low(Int8)),
    Int8(High(Int8))], ccReg, TypeInfo(Int8),
    True, False).AsInteger = High(Int8));

  { U16 }

  U16 := Low(UInt16);
  TValue.Make(@U16, TypeInfo(UInt16), V1);
  U16 := High(UInt16);
  TValue.Make(@U16, TypeInfo(UInt16), V2);
  Assert(Rtti.Invoke(@TestU16, [V1, V2], ccReg, TypeInfo(UInt16),
    True, False).AsInteger = High(UInt16));
  Assert(Rtti.Invoke(@TestManyU16, [UInt16(11), UInt16(22), UInt16(33),
    UInt16(44), UInt16(55), UInt16(66), UInt16(77), UInt16(88),
    UInt16(Low(UInt16)), UInt16(High(UInt16))], ccReg, TypeInfo(UInt16),
    True, False).AsInteger = High(UInt16));

  { S16 }

  S16 := Low(Int16);
  TValue.Make(@S16, TypeInfo(Int16), V1);
  S16 := High(Int16);
  TValue.Make(@S16, TypeInfo(Int16), V2);
  Assert(Rtti.Invoke(@TestS16, [V1, V2], ccReg, TypeInfo(Int16),
    True, False).AsInteger = High(Int16));
  Assert(Rtti.Invoke(@TestManyS16, [Int16(11), Int16(22), Int16(33), Int16(44),
    Int16(55), Int16(66), Int16(77), Int16(88), Int16(Low(Int16)),
    Int16(High(Int16))], ccReg, TypeInfo(Int16),
    True, False).AsInteger = High(Int16));

  { U32 }

  U32 := Low(UInt32);
  TValue.Make(@U32, TypeInfo(UInt32), V1);
  U32 := High(UInt32);
  TValue.Make(@U32, TypeInfo(UInt32), V2);
  Assert(Rtti.Invoke(@TestU32, [V1, V2], ccReg, TypeInfo(UInt32),
    True, False).AsInt64 = High(UInt32));
  Assert(Rtti.Invoke(@TestManyU32, [UInt32(11), UInt32(22), UInt32(33),
    UInt32(44), UInt32(55), UInt32(66), UInt32(77), UInt32(88),
    UInt32(Low(UInt32)), UInt32(High(UInt32))], ccReg, TypeInfo(UInt32),
    True, False).AsInt64 = High(UInt32));

  { S32 }

  S32 := Low(Int32);
  TValue.Make(@S32, TypeInfo(Int32), V1);
  S32 := High(Int32);
  TValue.Make(@S32, TypeInfo(Int32), V2);
  Assert(Rtti.Invoke(@TestS32, [V1, V2], ccReg, TypeInfo(Int32),
    True, False).AsInt64 = High(Int32));
  Assert(Rtti.Invoke(@TestManyS32, [Int32(11), Int32(22), Int32(33),
    Int32(44), Int32(55), Int32(66), Int32(77), Int32(88),
    Int32(Low(Int32)), Int32(High(Int32))], ccReg, TypeInfo(Int32),
    True, False).AsInt64 = High(Int32));

  { U64 }

  U64 := Low(UInt64);
  TValue.Make(@U64, TypeInfo(UInt64), V1);
  U64 := High(UInt64);
  TValue.Make(@U64, TypeInfo(UInt64), V2);
  Assert(Rtti.Invoke(@TestU64, [V1, V2], ccReg, TypeInfo(UInt64),
    True, False).AsUInt64 = High(UInt64));
  Assert(Rtti.Invoke(@TestManyU64, [UInt64(11), UInt64(22), UInt64(33),
    UInt64(44), UInt64(55), UInt64(66), UInt64(77), UInt64(88),
    UInt64(Low(UInt64)), UInt64(High(UInt64))], ccReg, TypeInfo(UInt64),
    True, False).AsUInt64 = High(UInt64));

  { S64 }

  S64 := Low(Int64);
  TValue.Make(@S64, TypeInfo(Int64), V1);
  S64 := High(Int64);
  TValue.Make(@S64, TypeInfo(Int64), V2);
  Assert(Rtti.Invoke(@TestS64, [V1, V2], ccReg, TypeInfo(Int64),
    True, False).AsInt64 = High(Int64));
  Assert(Rtti.Invoke(@TestManyS64, [Int64(11), Int64(22), Int64(33),
    Int64(44), Int64(55), Int64(66), Int64(77), Int64(88),
    Int64(Low(Int64)), Int64(High(Int64))], ccReg, TypeInfo(Int64),
    True, False).AsInt64 = High(Int64));

  { F32 }

  F32 := MinSingle;
  TValue.Make(@F32, TypeInfo(Single), V1);
  F32 := MaxSingle;
  TValue.Make(@F32, TypeInfo(Single), V2);
  Assert(EqF32(Rtti.Invoke(@TestF32, [V1, V2], ccReg, TypeInfo(Single),
    True, False).AsExtended, MaxSingle));
  Assert(EqF32(Rtti.Invoke(@TestManyF32, [Single(11.11), Single(22.22),
    Single(33.33), Single(44.44), Single(55.55), Single(66.66), Single(77.77),
    Single(88.88), Single(MinSingle), Single(MaxSingle)], ccReg,
    TypeInfo(Single), True, False).AsExtended, MaxSingle));

  { F64 }

  F64 := MinDouble;
  TValue.Make(@F64, TypeInfo(Double), V1);
  F64 := MaxDouble;
  TValue.Make(@F64, TypeInfo(Double), V2);
  Assert(EqF64(Rtti.Invoke(@TestF64, [V1, V2], ccReg, TypeInfo(Double),
    True, False).AsExtended, MaxDouble));
  Assert(EqF64(Rtti.Invoke(@TestManyF64, [Double(11.11), Double(22.22),
    Double(33.33), Double(44.44), Double(55.55), Double(66.66), Double(77.77),
    Double(88.88), Double(MinDouble), Double(MaxDouble)], ccReg,
    TypeInfo(Double), True, False).AsExtended, MaxDouble));

  { FCO }

  FCO := 123;
  TValue.Make(@FCO, TypeInfo(Comp), V1);
  FCO := 456;
  TValue.Make(@FCO, TypeInfo(Comp), V2);
  Assert(Rtti.Invoke(@TestFCO, [V1, V2], ccReg, TypeInfo(Int64{Comp}),
    True, False).AsInt64 = 789);

  { FCU }

  FCU := MinCurrency;
  TValue.Make(@FCU, TypeInfo(Currency), V1);
  FCU := MaxCurrency;
  TValue.Make(@FCU, TypeInfo(Currency), V2);
  Assert(EqF64(Rtti.Invoke(@TestFCU, [V1, V2], ccReg, TypeInfo(Currency),
    True, False).AsCurrency, MaxCurrency));
  Assert(EqF64(Rtti.Invoke(@TestManyFCU, [Currency(11.11), Currency(22.22),
    Currency(33.33), Currency(44.44), Currency(55.55), Currency(66.66),
    Currency(77.77), Currency(88.88), Currency(MinCurrency),
    Currency(MaxCurrency)], ccReg, TypeInfo(Currency),
    True, False).AsCurrency, MaxCurrency));

  { F80 }

  F80 := MinExtended;
  TValue.Make(@F80, TypeInfo(Extended), V1);
  F80 := MaxExtended;
  TValue.Make(@F80, TypeInfo(Extended), V2);
  Assert(EqF80(Rtti.Invoke(@TestF80, [V1, V2], ccReg, TypeInfo(Extended),
    True, False).AsExtended, MaxExtended));
  Assert(EqF80(Rtti.Invoke(@TestManyF80, [Extended(11.11), Extended(22.22),
    Extended(33.33), Extended(44.44), Extended(55.55), Extended(66.66),
    Extended(77.77), Extended(88.88), Extended(MinExtended),
    Extended(MaxExtended)], ccReg, TypeInfo(Extended),
    True, False).AsExtended, MaxExtended));

  { SS }

  SS := 'abc';
  TValue.Make(@SS[0], TypeInfo(ShortString), V1);
  SS := '123';
  TValue.Make(@SS[0], TypeInfo(ShortString), V2);
  Assert(Rtti.Invoke(@TestSS, [V1, V2], ccReg, TypeInfo(ShortString),
    True, False).AsString = 'abc123');
  SetLength(VA, 10);
  for I := Low(VA) to High(VA) do
  begin
    SS := Concat('S', I.ToString);
    TValue.Make(@SS[0], TypeInfo(ShortString), VA[I]);
  end;
  Assert(Rtti.Invoke(@TestManySS, VA, ccReg, TypeInfo(ShortString),
    True, False).AsString = 'abc123');

  { SA }

  SA := 'abc';
  TValue.Make(@SA, TypeInfo(AnsiString), V1);
  SA := '123';
  TValue.Make(@SA, TypeInfo(AnsiString), V2);
  Assert(Rtti.Invoke(@TestSA, [V1, V2], ccReg, TypeInfo(AnsiString),
    True, False).AsAnsiString = 'abc123');
  SetLength(VA, 10);
  for I := Low(VA) to High(VA) do
  begin
    SA := Concat('S', I.ToString);
    TValue.Make(@SA, TypeInfo(AnsiString), VA[I]);
  end;
  Assert(Rtti.Invoke(@TestManySA, VA, ccReg, TypeInfo(AnsiString),
    True, False).AsAnsiString = 'abc123');

  { SU }

  SU := 'abc';
  TValue.Make(@SU, TypeInfo(UnicodeString), V1);
  SU := '123';
  TValue.Make(@SU, TypeInfo(UnicodeString), V2);
  Assert(Rtti.Invoke(@TestSU, [V1, V2], ccReg, TypeInfo(UnicodeString),
    True, False).AsUnicodeString = 'abc123');
  SetLength(VA, 10);
  for I := Low(VA) to High(VA) do
  begin
    SU := Concat('S', UnicodeString(I.ToString));
    TValue.Make(@SU, TypeInfo(UnicodeString), VA[I]);
  end;
  Assert(Rtti.Invoke(@TestManySU, VA, ccReg, TypeInfo(UnicodeString),
    True, False).AsUnicodeString = 'abc123');

  { SW }

  SW := 'abc';
  TValue.Make(@SW, TypeInfo(WideString), V1);
  SW := '123';
  TValue.Make(@SW, TypeInfo(WideString), V2);
  Assert(Rtti.Invoke(@TestSW, [V1, V2], ccReg, TypeInfo(WideString),
    True, False).AsString = 'abc123');
  SetLength(VA, 10);
  for I := Low(VA) to High(VA) do
  begin
    SW := Concat('S', WideString(I.ToString));
    TValue.Make(@SW, TypeInfo(WideString), VA[I]);
  end;
  Assert(Rtti.Invoke(@TestManySW, VA, ccReg, TypeInfo(WideString),
    True, False).AsString = 'abc123');

  { P }

  P := @GVal1;
  TValue.Make(@P, TypeInfo(Pointer), V1);
  P := @GVal2;
  TValue.Make(@P, TypeInfo(Pointer), V2);
  Assert(Pointer(Rtti.Invoke(@TestP, [V1, V2], ccReg, TypeInfo(Int64{Pointer}),
    True, False).GetReferenceToRawData^) = @GVal3);
  Assert(Pointer(Rtti.Invoke(@TestManyP, [@GVal1, @GVal2, @GVal3, @GVal1,
    @GVal2, @GVal3, @GVal1, @GVal2, @GVal3, @GVal1], ccReg, TypeInfo(Int64),
    True, False).GetReferenceToRawData^) = @GVal3);

  { AD }

  AD := [123, 456];
  TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V1);
  AD := nil;
  SetLength(AD, 3);
  AD[0] := 789;
  AD[1] := 654;
  AD[2] := 321;
  TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V2);
  Assert(EqArr(TArrayOfLongIntDyn(Rtti.Invoke(@TestAD, [V1, V2], ccReg,
    TypeInfo(TArrayOfLongIntDyn), True, False).GetReferenceToRawData^),
    [147, 258, 369]));

  AD := [123, 456];
  TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V1);
  AD := [789, 654, 321];
  TValue.Make(@AD, TypeInfo(TArrayOfLongIntDyn), V2);
  Assert(EqArr(TArrayOfLongIntDyn(Rtti.Invoke(@TestManyAD, [V1, V2, V1, V2, V1,
    V2, V1, V2, V1, V2], ccReg, TypeInfo(TArrayOfLongIntDyn),
    True, False).GetReferenceToRawData^), [963, 852, 741]));

  { AS }

  AS_[0] := 123;
  AS_[1] := 456;
  AS_[2] := 0;
  TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V1);
  AS_[0] := 789;
  AS_[1] := 654;
  AS_[2] := 321;
  TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V2);
  Assert(EqArr(TArrayOfLongIntStatic(Rtti.Invoke(@TestAS, [V1, V2], ccReg,
    TypeInfo(TArrayOfLongIntStatic), True, False).GetReferenceToRawData^),
    [147, 258, 369]));

  AS_[0] := 123;
  AS_[1] := 456;
  AS_[2] := 0;
  TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V1);
  AS_[0] := 789;
  AS_[1] := 654;
  AS_[2] := 321;
  TValue.Make(@AS_, TypeInfo(TArrayOfLongIntStatic), V2);
  Assert(EqArr(TArrayOfLongIntStatic(Rtti.Invoke(@TestManyAS, [V1, V2, V1, V2,
    V1, V2, V1, V2, V1, V2], ccReg, TypeInfo(TArrayOfLongIntStatic),
    True, False).GetReferenceToRawData^), [963, 852, 741]));

  { E }

  E := Low(TTestEnum);
  TValue.Make(@E, TypeInfo(TTestEnum), V1);
  E := High(TTestEnum);
  TValue.Make(@E, TypeInfo(TTestEnum), V2);
  Assert(TTestEnum(Rtti.Invoke(@TestE, [V1, V2], ccReg, TypeInfo(TTestEnum),
    True, False).GetReferenceToRawData^) = High(TTestEnum));
  Assert(TTestEnum(Rtti.Invoke(@TestManyE, [V1, V2, V1, V2, V1, V2, V1, V2,
    V1, V2], ccReg, TypeInfo(TTestEnum), True, False).GetReferenceToRawData^) =
    High(TTestEnum));

  { S }

  S := [te1];
  TValue.Make(@S, TypeInfo(TTestSet), V1);
  S := [te10];
  TValue.Make(@S, TypeInfo(TTestSet), V2);
  Assert(TTestSet(Rtti.Invoke(@TestS, [V1, V2], ccReg, TypeInfo(TTestSet),
    True, False).GetReferenceToRawData^) = [te1, te2, te3]);
  Assert(TTestSet(Rtti.Invoke(@TestManyS, [V1, V2, V1, V2, V1, V2, V1, V2,
    V1, V2], ccReg, TypeInfo(TTestSet), True, False).GetReferenceToRawData^) =
    [te8, te9, te10]);

  { Static proc }

  Proc := @TestProcedure1;
  TValue.Make(@@Proc, TypeInfo(TTestProc), V1);
  Proc := @TestProcedure2;
  TValue.Make(@@Proc, TypeInfo(TTestProc), V2);
  GProcOK1 := False;
  GProcOK2 := True;
  Proc := TTestProc(Rtti.Invoke(@TestProc, [V1, V2], ccReg, TypeInfo(TTestProc),
    True, False).GetReferenceToRawData^);
  Assert(GProcOK1);
  Assert(not GProcOK2);
  GProcOK1 := False;
  Proc;
  Assert(GProcOK1);
  GProcOK1 := False;
  GProcOK2 := True;
  Proc := TTestProc(Rtti.Invoke(@TestManyProc, [V1, V2, V1, V2, V1, V2, V1, V2,
    V1, V2], ccReg, TypeInfo(TTestProc), True, False).GetReferenceToRawData^);
  Assert(GProcOK1);
  Assert(not GProcOK2);
  GProcOK1 := False;
  Proc;
  Assert(GProcOK1);

  { Static func 1 }

  Func1 := @TestFunction1;
  TValue.Make(@@Func1, TypeInfo(TTestFunc1), V1);
  Func1 := @TestFunction2;
  TValue.Make(@@Func1, TypeInfo(TTestFunc1), V2);
  Func1 := TTestFunc1(Rtti.Invoke(@TestFunc1, [V1, V2], ccReg,
    TypeInfo(TTestFunc1), True, False).GetReferenceToRawData^);
  Assert(Func1 = 123);
  Func1 := TTestFunc1(Rtti.Invoke(@TestManyFunc1, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(TTestFunc1), True,
    False).GetReferenceToRawData^);
  Assert(Func1 = 456);

  { Static func 2 }

  Func2 := @TestFunction3;
  TValue.Make(@@Func2, TypeInfo(TTestFunc2), V1);
  Func2 := @TestFunction4;
  TValue.Make(@@Func2, TypeInfo(TTestFunc2), V2);
  Func2 := TTestFunc2(Rtti.Invoke(@TestFunc2, [V1, V2], ccReg,
    TypeInfo(TTestFunc2), True, False).GetReferenceToRawData^);
  Assert(Func2(123, [456, 789]) = '123456789');
  Func2 := TTestFunc2(Rtti.Invoke(@TestManyFunc2, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(TTestFunc2), True,
    False).GetReferenceToRawData^);
  Assert(Func2(123, [456, 789]) = '123456789abc123');

  { Method }

  TestMethodObj := TTestMethodObj.Create;
  try
    Method1 := TestMethodObj.TestMethod1;
    TValue.Make(@@Method1, TypeInfo(TTestMethod1), V1);
    Method1 := TestMethodObj.TestMethod2;
    TValue.Make(@@Method1, TypeInfo(TTestMethod1), V2);
    GProcOK1 := False;
    GProcOK2 := True;
    Method1 := TTestMethod1(Rtti.Invoke(@TestMethod1, [TestMethodObj, V1, V2],
      ccReg, TypeInfo(TTestMethod1), True, False).GetReferenceToRawData^);
    Assert(GProcOK1);
    Assert(not GProcOK2);
    GProcOK1 := False;
    Method1;
    Assert(GProcOK1);
    GProcOK1 := False;
    GProcOK2 := True;
    Method1 := TTestMethod1(Rtti.Invoke(@TestManyMethod1, [TestMethodObj, V1,
      V2, V1, V2, V1, V2, V1, V2, V1, V2], ccReg, TypeInfo(TTestMethod1),
      True, False).GetReferenceToRawData^);
    Assert(GProcOK1);
    Assert(not GProcOK2);
    GProcOK1 := False;
    Method1;
    Assert(GProcOK1);
  finally
    TestMethodObj.Destroy;
  end;

  { R1 }

  R1.Member1 := 123;
  R1.Member2 := 456;
  TValue.Make(@R1, TypeInfo(TTestRecord1), V1);
  R1.Member1 := 987;
  R1.Member2 := 654;
  TValue.Make(@R1, TypeInfo(TTestRecord1), V2);
  R1 := TTestRecord1(Rtti.Invoke(@TestRecord1, [V1, V2], ccReg,
    TypeInfo(TTestRecord1), True, False).GetReferenceToRawData^);
  Assert((R1.Member1 = 147) and (R1.Member2 = 258));
  R1 := TTestRecord1(Rtti.Invoke(@TestManyRecord1, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(TTestRecord1), True,
    False).GetReferenceToRawData^);
  Assert((R1.Member1 = 258) and (R1.Member2 = 147));

  { R2 }

  R2.Member1 := 'abc';
  R2.Member2 := 123;
  TValue.Make(@R2, TypeInfo(TTestRecord2), V1);
  R2.Member1 := 'def';
  R2.Member2 := 456;
  TValue.Make(@R2, TypeInfo(TTestRecord2), V2);
  R2 := TTestRecord2(Rtti.Invoke(@TestRecord2, [V1, V2], ccReg,
    TypeInfo(TTestRecord2), True, False).GetReferenceToRawData^);
  Assert((R2.Member1 = 'ghi') and (R2.Member2 = 789));
  R2 := TTestRecord2(Rtti.Invoke(@TestManyRecord2, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(TTestRecord2), True,
    False).GetReferenceToRawData^);
  Assert((R2.Member1 = 'xyz') and (R2.Member2 = 951));

  { R3 }

  R3.Member1 := 11;
  R3.Member2 := 22.22;
  R3.Member3 := 'aaa';
  R3.Member4 := 33.33;
  TValue.Make(@R3, TypeInfo(TTestRecord3), V1);
  R3.Member1 := 22;
  R3.Member2 := 44.44;
  R3.Member3 := 'bbb';
  R3.Member4 := 55.55;
  TValue.Make(@R3, TypeInfo(TTestRecord3), V2);
  R3 := TTestRecord3(Rtti.Invoke(@TestRecord3, [V1, V2], ccReg,
    TypeInfo(TTestRecord3), True, False).GetReferenceToRawData^);
  Assert((R3.Member1 = 123) and EqF64(R3.Member2, 12.34) and
    (R3.Member3 = 'abc') and EqF80(R3.Member4, 45.67));
  R3 := TTestRecord3(Rtti.Invoke(@TestManyRecord3, [V1, V2, V1, V2, V1, V2,
    V1, V2, V1, V2], ccReg, TypeInfo(TTestRecord3), True,
    False).GetReferenceToRawData^);
  Assert((R3.Member1 = 321) and EqF64(R3.Member2, 43.21) and
    (R3.Member3 = 'def') and EqF80(R3.Member4, 76.54));

  { C1 }

  C1 := TTestClass1.Create;
  C12 := TTestClass1.Create;
  try
    C1.Field1 := 123;
    C1.Field2 := 456;
    TValue.Make(@C1, TypeInfo(TTestClass1), V1);
    C12.Field1 := 987;
    C12.Field2 := 654;
    TValue.Make(@C12, TypeInfo(TTestClass1), V2);
    C1R := TTestClass1(Rtti.Invoke(@TestClass1, [V1, V2], ccReg,
      TypeInfo(TTestClass1), True, False).GetReferenceToRawData^);
    try
      Assert((C1R.Field1 = 147) and (C1R.Field2 = 258));
    finally
      C1R.Free;
    end;
    C1R := TTestClass1(Rtti.Invoke(@TestManyClass1, [V1, V2, V1, V2, V1, V2, V1,
      V2, V1, V2], ccReg, TypeInfo(TTestClass1), True,
      False).GetReferenceToRawData^);
    try
      Assert((C1R.Field1 = 258) and (C1R.Field2 = 147));
    finally
      C1R.Free;
    end;
  finally
    C1.Destroy;
    C12.Destroy;
  end;

  { C2 }

  C2 := TTestClass2.Create;
  C22 := TTestClass2.Create;
  try
    C2.Field1 := 'abc';
    C2.Field2 := 123;
    TValue.Make(@C2, TypeInfo(TTestClass2), V1);
    C22.Field1 := 'def';
    C22.Field2 := 456;
    TValue.Make(@C22, TypeInfo(TTestClass2), V2);
    C2R := TTestClass2(Rtti.Invoke(@TestClass2, [V1, V2], ccReg,
      TypeInfo(TTestClass2), True, False).GetReferenceToRawData^);
    try
      Assert((C2R.Field1 = 'ghi') and (C2R.Field2 = 789));
    finally
      C2R.Free;
    end;
    C2R := TTestClass2(Rtti.Invoke(@TestManyClass2, [V1, V2, V1, V2, V1, V2, V1,
      V2, V1, V2], ccReg, TypeInfo(TTestClass2), True,
      False).GetReferenceToRawData^);
    try
      Assert((C2R.Field1 = 'xyz') and (C2R.Field2 = 951));
    finally
      C2R.Free;
    end;
  finally
    C2.Destroy;
    C22.Destroy;
  end;

  { C3 }

  C3 := TTestClass3.Create;
  C32 := TTestClass3.Create;
  try
    C3.Field1 := 11;
    C3.Field2 := 22.22;
    C3.Field3 := 'aaa';
    C3.Field4 := 33.33;
    TValue.Make(@C3, TypeInfo(TTestClass3), V1);
    C32.Field1 := 22;
    C32.Field2 := 44.44;
    C32.Field3 := 'bbb';
    C32.Field4 := 55.55;
    TValue.Make(@C32, TypeInfo(TTestClass3), V2);
    C3R := TTestClass3(Rtti.Invoke(@TestClass3, [V1, V2], ccReg,
      TypeInfo(TTestClass3), True, False).GetReferenceToRawData^);
    try
      Assert((C3R.Field1 = 123) and EqF64(C3R.Field2, 12.34) and
        (C3R.Field3 = 'abc') and EqF80(C3R.Field4, 45.67));
    finally
      C3R.Free;
    end;
    C3R := TTestClass3(Rtti.Invoke(@TestManyClass3, [V1, V2, V1, V2, V1, V2,
      V1, V2, V1, V2], ccReg, TypeInfo(TTestClass3), True,
      False).GetReferenceToRawData^);
    try
      Assert((C3R.Field1 = 321) and EqF64(C3R.Field2, 43.21) and
        (C3R.Field3 = 'def') and EqF80(C3R.Field4, 76.54));
    finally
      C3R.Free
    end;
  finally
    C3.Destroy;
    C32.Destroy;
  end;

  { I1 }

  I1 := TTestIntf1.Create;
  I1.Field1 := 123;
  I1.Field2 := 456;
  TValue.Make(@I1, TypeInfo(ITestIntf1), V1);
  I12 := TTestIntf1.Create;
  I12.Field1 := 987;
  I12.Field2 := 654;
  TValue.Make(@I12, TypeInfo(ITestIntf1), V2);
  I1R := ITestIntf1(Rtti.Invoke(@TestIntf1, [V1, V2], ccReg,
    TypeInfo(ITestIntf1), True, False).GetReferenceToRawData^);
  Assert((I1R.Field1 = 147) and (I1R.Field2 = 258));
  I1R := ITestIntf1(Rtti.Invoke(@TestManyIntf1, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(ITestIntf1), True,
    False).GetReferenceToRawData^);
  Assert((I1R.Field1 = 258) and (I1R.Field2 = 147));

  { I2 }

  I2 := TTestIntf2.Create;
  I2.Field1 := 'abc';
  I2.Field2 := 123;
  TValue.Make(@I2, TypeInfo(ITestIntf2), V1);
  I22 := TTestIntf2.Create;
  I22.Field1 := 'def';
  I22.Field2 := 456;
  TValue.Make(@I22, TypeInfo(ITestIntf2), V2);
  I2R := ITestIntf2(Rtti.Invoke(@TestIntf2, [V1, V2], ccReg,
    TypeInfo(ITestIntf2), True, False).GetReferenceToRawData^);
  Assert((I2R.Field1 = 'ghi') and (I2R.Field2 = 789));
  I2R := ITestIntf2(Rtti.Invoke(@TestManyIntf2, [V1, V2, V1, V2, V1, V2, V1,
    V2, V1, V2], ccReg, TypeInfo(ITestIntf2), True,
    False).GetReferenceToRawData^);
  Assert((I2R.Field1 = 'xyz') and (I2R.Field2 = 951));

  { I3 }

  I3 := TTestIntf3.Create;
  I3.Field1 := 11;
  I3.Field2 := 22.22;
  I3.Field3 := 'aaa';
  I3.Field4 := 33.33;
  TValue.Make(@I3, TypeInfo(ITestIntf3), V1);
  I32 := TTestIntf3.Create;
  I32.Field1 := 22;
  I32.Field2 := 44.44;
  I32.Field3 := 'bbb';
  I32.Field4 := 55.55;
  TValue.Make(@I32, TypeInfo(ITestIntf3), V2);
  I3R := ITestIntf3(Rtti.Invoke(@TestIntf3, [V1, V2], ccReg,
    TypeInfo(ITestIntf3), True, False).GetReferenceToRawData^);
  Assert((I3R.Field1 = 123) and EqF64(I3R.Field2, 12.34) and
    (I3R.Field3 = 'abc') and EqF80(I3R.Field4, 45.67));
  I3R := ITestIntf3(Rtti.Invoke(@TestManyIntf3, [V1, V2, V1, V2, V1, V2,
    V1, V2, V1, V2], ccReg, TypeInfo(ITestIntf3), True,
    False).GetReferenceToRawData^);
  Assert((I3R.Field1 = 321) and EqF64(I3R.Field2, 43.21) and
    (I3R.Field3 = 'def') and EqF80(I3R.Field4, 76.54));

  WriteLn('All tests OK');
end.
tests.pp (59,385 bytes)

Sven Barth

2019-01-02 23:09

manager  

rtti-sysv-invoke.patch (23,312 bytes)
��diff --git a/packages/rtl-objpas/src/inc/rtti.pp b/packages/rtl-objpas/src/inc/rtti.pp

index d1e42241..8cab3462 100644

--- a/packages/rtl-objpas/src/inc/rtti.pp

+++ b/packages/rtl-objpas/src/inc/rtti.pp

@@ -3552,7 +3552,7 @@ begin

 end;}

 

 {$ifndef InLazIDE}

-{$if defined(CPUX86_64) and defined(WIN64)}

+{$if defined(CPUX86_64)}

 {$I invoke.inc}

 {$endif}

 {$endif}

diff --git a/packages/rtl-objpas/src/x86_64/invoke.inc b/packages/rtl-objpas/src/x86_64/invoke.inc

index b64c2a78..c155de43 100644

--- a/packages/rtl-objpas/src/x86_64/invoke.inc

+++ b/packages/rtl-objpas/src/x86_64/invoke.inc

@@ -1,7 +1,8 @@

 {

   This file is part of the Free Pascal run time library.

-  Copyright (C) 2018 Sven Barth

-  member of the Free Pascal development team.

+  Copyright (C) 2018 Sven Barth (Win64)

+  Copyright (C) 2018 Silvio Clecio (silvioprog) (Sys V)

+  members of the Free Pascal development team.

 

   Function call manager for x86_64

 

@@ -80,6 +81,166 @@ asm

   movq 16(%rsp), %rsi

   movq 8(%rsp), %rbp

 end;

+{$else}

+const

+  FPREGS_COUNT = 8;

+  GPREGS_COUNT = 6;

+

+  INTEGER_CLASS = 0;

+  SSE_CLASS = 1;

+  X87_CLASS = 2;

+  MEMORY_CLASS = 3;

+

+type

+  TStackItem = packed record

+    Value: PtrUInt; // 0-8

+    Size: SizeInt; // 8-16

+    ByRef: Boolean; // 16-17

+    ArgCls: Byte; // 17-18

+  end;

+

+  TResultType = type Byte;

+

+  TInvokeHandle = packed record

+    FPRegs: array[0..Pred(FPREGS_COUNT)] of PtrUInt; // 0-64

+    GPRegs: array[0..Pred(GPREGS_COUNT)] of PtrUInt; // 64-112

+    StackAddr: Pointer; // 112-120

+    StackCount: PtrUInt; // 120-128

+    StackSize: SizeUInt; // 128-136

+    FuncAddr: CodePointer; // 136-144

+    ResultType: TResultType; // 1144-145

+    Result: PtrUInt; // 145-153

+  end;

+

+procedure InvokeKernelSysV(var AHandle: TInvokeHandle); assembler; nostackframe;

+label

+  get_stack_item, check_stack_items, get_extra_arg, check_extra_args, by_ref,

+    mem_cls, exit;

+asm

+  { save base pointer; set new base pointer to rsp }

+  push %rbp

+  mov %rsp, %rbp

+

+  { save callee-saved registers }

+  push %rbx

+  push %r12

+  push %r13

+  push %r14

+  push %r15

+

+  { save invoke handle }

+  mov %rdi, %r15

+

+  { allocate extra arguments space }

+  mov 128(%r15), %rcx // Handle.StackSize

+  sub %rcx, %rsp

+

+  { iterate stack items }

+  mov 112(%r15), %rsi // Handle.StackAddr

+  mov $-8, %r13

+  mov $-18, %rax

+  mov 120(%r15), %rcx // Handle.StackCount

+  jmp check_stack_items

+get_stack_item:

+  add $18, %rax

+  lea (%rsi, %rax), %rdi

+  mov (%rdi), %r9 // Item.Value

+  mov 8(%rdi), %r8 // Item.Size

+  movb 16(%rdi), %r12b // Item.ByRef

+  movb 17(%rdi), %dl // Item.ArgCls

+  { get stack item values }

+  mov $-8, %r14

+  jmp check_extra_args

+get_extra_arg:

+  add $8, %r13

+  add $8, %r14

+  cmpb $1, %r12b

+  je by_ref

+  { INTEGER/SSE CLASS }

+  lea (%r9, %r14), %rbx

+  mov %rbx, (%rsp, %r13)

+  dec %rcx

+  jmp check_stack_items

+by_ref:

+  cmpb MEMORY_CLASS, %dl

+  je mem_cls

+  { POINTER/X87 CLASS }

+  mov (%r9, %r14), %rbx

+  mov %rbx, (%rsp, %r13)

+  sub $8, %r8

+  jmp check_extra_args

+mem_cls:

+  { MEMORY CLASS }

+  sub $8, %r8

+  mov (%r9, %r14), %rbx

+  mov %rbx, (%rsp, %r13)

+  cmp $0, %r8

+  jg get_extra_arg

+  dec %rcx

+  jmp check_stack_items

+check_extra_args:

+  cmp $0, %r8

+  jge get_extra_arg

+  dec %rcx

+check_stack_items:

+  cmp $0, %rcx

+  jnz get_stack_item

+

+  { setup general purpose registers }

+  lea 64(%r15), %rbx // Handle.GPRegs

+  mov (%rbx), %rdi

+  mov 8(%rbx), %rsi

+  mov 16(%rbx), %rdx

+  mov 24(%rbx), %rcx

+  mov 32(%rbx), %r8

+  mov 40(%rbx), %r9

+

+  { setup floating point registers }

+  lea (%r15), %rbx // Handle.FPRegs

+  movq (%rbx), %xmm0

+  movq 8(%rbx), %xmm1

+  movq 16(%rbx), %xmm2

+  movq 24(%rbx), %xmm3

+  movq 32(%rbx), %xmm4

+  movq 40(%rbx), %xmm5

+  movq 48(%rbx), %xmm6

+  movq 56(%rbx), %xmm7

+

+  { invoke user function }

+  mov $0, %rax

+  call *136(%r15) // Handle.FuncAddr

+

+  { get result type }

+  movb 144(%r15), %cl // Handle.ResultType

+

+  { return 64-bit value }

+  mov %rax, 145(%r15) // Handle.Result

+  cmpb INTEGER_CLASS, %cl

+  je exit

+

+  { return 80-bit float }

+  cmpb X87_CLASS, %cl

+  jne exit

+  fstpt 145(%r15) // Handle.Result

+

+exit:

+

+  { deallocate extra arguments space }

+  mov 128(%r15), %rcx // Handle.StackSize

+  add %rcx, %rsp

+

+  { restore callee-saved registers }

+  pop %r15

+  pop %r14

+  pop %r13

+  pop %r12

+  pop %rbx

+

+  { reset stack to base pointer; restore old base pointer }

+  leave

+  { return to caller }

+  ret

+end;

 {$endif}

 

 resourcestring

@@ -131,6 +292,7 @@ type

   PBoolean64 = ^Boolean64;

   PByteBool = ^ByteBool;

   PQWordBool = ^QWordBool;

+{$ifdef windows}

 var

   stackarea: array of PtrUInt;

   stackptr: Pointer;

@@ -140,6 +302,132 @@ var

   td: PTypeData;

   retinparam: Boolean;

   argcount, resreg: SizeInt;

+{$else}

+var

+  VArg: TFunctionCallParameter;

+  VHandle: TInvokeHandle;

+  VStack: array of TStackItem;

+  VRetReg, VRetSize: SizeInt;

+  I, VGPRegIdx, VFPRegIdx: LongInt;

+  VRetInParam: Boolean;

+

+  function PushRegVal(AValue: PtrUInt;

+    AArgCls: Byte = INTEGER_CLASS): Boolean;

+  begin

+    case AArgCls of

+      INTEGER_CLASS:

+      begin

+        if VGPRegIdx = VRetReg then

+          Inc(VGPRegIdx);

+        if VGPRegIdx < GPREGS_COUNT then

+        begin

+          VHandle.GPRegs[VGPRegIdx] := AValue;

+          Inc(VGPRegIdx);

+          Inc(VHandle.StackSize, 8);

+          Exit(True);

+        end;

+      end;

+      SSE_CLASS:

+        if VFPRegIdx < FPREGS_COUNT then

+        begin

+          VHandle.FPRegs[VFPRegIdx] := AValue;

+          Inc(VFPRegIdx);

+          Inc(VHandle.StackSize, 8);

+          Exit(True);

+        end;

+    end;

+    Result := False;

+  end;

+

+  procedure PushStackVal(AValue: PtrUInt; ASize: SizeInt;

+    AByRef: Boolean = False; AArgCls: Byte = INTEGER_CLASS);

+  var

+    VStackItem: TStackItem;

+  begin

+    Inc(VHandle.StackCount);

+    SetLength(VStack, VHandle.StackCount);

+    VStackItem.ByRef := AByRef;

+    VStackItem.Size := ASize;

+    VStackItem.Value := AValue;

+    VStackItem.ArgCls := AArgCls;

+    VStack[Pred(VHandle.StackCount)] := VStackItem;

+    if AArgCls = MEMORY_CLASS then

+      Inc(VHandle.StackSize, ASize)

+    else

+      Inc(VHandle.StackSize, 16); // stack aligned on 16 bytes boundary

+  end;

+

+  function PushVal(AValue: PtrUInt; AArgCls: Byte = INTEGER_CLASS;

+    AByRef: Boolean = False): Boolean; inline;

+  begin

+    Result := PushRegVal(AValue, AArgCls);

+    if not Result then

+      PushStackVal(AValue, 8, AByRef);

+  end;

+

+  function MapVal(ATD: PTypeData; AKind: TTypeKind;

+    AValue: Pointer): Boolean;

+  var

+    VField: PManagedField;

+    VTD: PTypeData;

+    I: SizeUInt;

+  begin

+    Result := True;

+    case AKind of

+      tkInteger, tkEnumeration: PushVal(PtrUInt(AValue^));

+      tkFloat:

+        case ATD^.FloatType of

+          ftSingle, ftDouble: PushVal(PtrUInt(AValue^), SSE_CLASS);

+          ftExtended: PushStackVal(PtrUInt(AValue), 10, True);

+          ftComp, ftCurr: PushVal(PtrUInt(AValue^));

+        end;

+      tkSet:

+        if ATD^.SetSize <= 8 then

+          PushVal(PtrUInt(AValue^))

+        else

+          if not PushRegVal(PtrUInt(AValue)) then

+            PushStackVal(PtrUInt(AValue), ATD^.SetSize);

+      tkMethod:

+      begin

+        PushRegVal(PtrUInt(AValue^));

+        if not PushRegVal(PtrUInt(AValue^) + 8) then

+          PushStackVal(PtrUInt(AValue), 8, True);

+      end;

+      tkSString: PushVal(PtrUInt(AValue));

+      tkAString, tkWString, tkInterface, tkClass, tkObject, tkUString,

+        tkDynArray, tkInterfaceRaw, tkProcVar, tkClassRef,

+        tkPointer: PushVal(PtrUInt(AValue^));

+      tkArray:

+        if ATD^.ArrayData.Size <= 8 then

+          PushVal(PtrUInt(AValue^))

+        else

+          if not PushRegVal(PtrUInt(AValue)) then

+            PushStackVal(PtrUInt(AValue), ATD^.ArrayData.Size);

+      tkRecord:

+        if ATD^.RecSize <= 8 then

+          PushVal(PtrUInt(AValue^))

+        else

+          if ATD^.RecSize <= 16 then

+          begin

+            VField := PManagedField(PByte(@ATD^.TotalFieldCount) + 4);

+            for I := 0 to Pred(ATD^.TotalFieldCount) do

+            begin

+              VTD := GetTypeData(VField^.TypeRef);

+              MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);

+              if ((VGPRegIdx = GPREGS_COUNT) or (VFPRegIdx = FPREGS_COUNT)) and

+                (VHandle.StackCount = 0) then

+                MapVal(VTD, VField^.TypeRef^.Kind, AValue + VField^.FldOffset);

+              Inc(VField);

+            end;

+          end

+          else

+            PushStackVal(PtrUInt(AValue), ATD^.RecSize, True, MEMORY_CLASS);

+      tkBool, tkInt64, tkQWord: PushVal(PtrUInt(AValue^));

+    else

+      Result := False;

+    end;

+  end;

+{$endif}

 begin

   if Assigned(aResultType) and not Assigned(aResultValue) then

     raise EInvocationError.Create(SErrInvokeResultTypeNoValue);

@@ -287,7 +575,57 @@ begin

     end;

   end;

 {$else}

-  raise EInvocationError.Create(SErrPlatformNotSupported);

+  VRetInParam := False;

+  if Assigned(AResultType) then

+  begin

+    if not Assigned(AResultValue) then

+      raise EInvocationError.Create(SErrInvokeResultTypeNoValue);

+    case AResultType^.Kind of

+      tkSString, tkAString, tkWString, tkArray, tkInterface, tkDynArray,

+        tkUString: VRetInParam := True;

+      tkRecord: VRetInParam := GetTypeData(AResultType)^.RecSize > 8;

+    end;

+  end;

+  VStack := nil;

+  VHandle := Default(TInvokeHandle);

+  VGPRegIdx := 0;

+  VFPRegIdx := 0;

+  if VRetInParam then

+  begin

+    if fcfStatic in AFlags then

+      VRetReg := 0

+    else

+      VRetReg := 1;

+    VHandle.GPRegs[VRetReg] := PtrUInt(AResultValue);

+  end

+  else

+    VRetReg := -1;

+  for I := 0 to High(AArgs) do

+  begin

+    VArg := AArgs[I];

+    if VArg.Info.ParamFlags * [pfArray, pfOut, pfVar, pfConstRef] <> [] then

+      PushVal(PtrUInt(VArg.ValueRef))

+    else

+      if not MapVal(GetTypeData(VArg.Info.ParamType),

+        VArg.Info.ParamType^.Kind, VArg.ValueRef) then

+        raise EInvocationError.CreateFmt(SErrFailedToConvertArg,

+          [I, VArg.Info.ParamType^.Name]);

+  end;

+  VHandle.StackAddr := @VStack[0];

+  VHandle.FuncAddr := aCodeAddress;

+  if AResultType = TypeInfo(Extended) then

+  begin

+    VHandle.ResultType := X87_CLASS;

+    VRetSize := 10;

+  end

+  else

+  begin

+    VHandle.ResultType := INTEGER_CLASS;

+    VRetSize := 8;

+  end;

+  InvokeKernelSysV(VHandle);

+  if Assigned(AResultType) and not VRetInParam then

+    Move(VHandle.Result, PPtrUInt(AResultValue)^, VRetSize);

 {$endif}

 end;

 

diff --git a/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp b/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp

index 90ddc3b8..c35e6243 100644

--- a/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp

+++ b/packages/rtl-objpas/tests/testrunner.rtlobjpas.pp

@@ -6,8 +6,11 @@ program testrunner.rtlobjpas;

 {$mode objfpc}{$H+}

 { Invoke needs a function call manager }

 {.$define useffi}

-{$if defined(CPUX64) and defined(WINDOWS)}

+{$if defined(CPUX64)}

 {$define testinvoke}

+{$if defined(WINDOWS)}

+{$define testimpl}

+{$endif}

 {$else}

 {$ifdef useffi}

 {$define testinvoke}

rtti-sysv-invoke.patch (23,312 bytes)

Sven Barth

2019-01-02 23:10

manager   ~0113114

Last edited: 2019-01-02 23:11

View 2 revisions

Thank you for your work so far.

I've integrated your code into packages/rtl-objpas/src/x86_64/invoke.inc (see attached patch), but the tests in packages/rtl-objpas/tests/tests.rtti.invoke.pas fail (called with --suite=TTestInvoke):

=== output begin ===

<?xml version="1.0" encoding="utf-8"?>
<TestResults>
  <!-- Generated using FPCUnit on 2019-01-02 22:46:53-->
  <TestListing>
    <TestSuite Name="SuiteList" ElapsedTime="00:00:00.004" NumberOfErrors="0" NumberOfFailures="8" NumberOfRunTests="15" NumberOfIgnoredTests="0">
      <TestSuite Name="TTestInvoke" ElapsedTime="00:00:00.004" NumberOfErrors="0" NumberOfFailures="8" NumberOfRunTests="15" NumberOfIgnoredTests="0">
        <Test Name="TestShortString" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestAnsiString" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestWideString" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestUnicodeString" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestLongInt" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestInt64" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestTObject" Result="OK" ElapsedTime="00:00:00.000"/>
        <Test Name="TestIntfMethods" Result="Failed" ElapsedTime="00:00:00.002">
          <Message>TTestInvoke.TestIntfMethods: Reported result value differs from returned for Test13</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for Test13</ExceptionMessage>
        </Test>
        <Test Name="TestIntfMethodsRecs" Result="Failed" ElapsedTime="00:00:00.001">
          <Message>TTestInvoke.TestIntfMethodsRecs: Reported result value differs from returned for TestRecSize9</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for TestRecSize9</ExceptionMessage>
        </Test>
        <Test Name="TestMethodVars" Result="Failed" ElapsedTime="00:00:00.000">
          <Message>TTestInvoke.TestMethodVars: Reported result value differs from returned for Test13</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for Test13</ExceptionMessage>
        </Test>
        <Test Name="TestMethodVarsRecs" Result="Failed" ElapsedTime="00:00:00.000">
          <Message>TTestInvoke.TestMethodVarsRecs: Reported result value differs from returned for TestRecSize9</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for TestRecSize9</ExceptionMessage>
        </Test>
        <Test Name="TestProcVars" Result="Failed" ElapsedTime="00:00:00.001">
          <Message>TTestInvoke.TestProcVars: Reported result value differs from returned for Test13</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for Test13</ExceptionMessage>
        </Test>
        <Test Name="TestProcVarsRecs" Result="Failed" ElapsedTime="00:00:00.000">
          <Message>TTestInvoke.TestProcVarsRecs: Reported result value differs from returned for TestRecSize9</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for TestRecSize9</ExceptionMessage>
        </Test>
        <Test Name="TestProc" Result="Failed" ElapsedTime="00:00:00.000">
          <Message>TTestInvoke.TestProc: Reported result value differs from returned for Test18</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for Test18</ExceptionMessage>
        </Test>
        <Test Name="TestProcRecs" Result="Failed" ElapsedTime="00:00:00.000">
          <Message>TTestInvoke.TestProcRecs: Reported result value differs from returned for TestRecSize9</Message>
          <ExceptionClass>EAssertionFailedError</ExceptionClass>
          <ExceptionMessage>Reported result value differs from returned for TestRecSize9</ExceptionMessage>
        </Test>
      </TestSuite>
    </TestSuite>
  </TestListing>
  <Title>RTL-ObjPas unit tests</Title>
  <NumberOfRunTests>15</NumberOfRunTests>
  <NumberOfErrors>0</NumberOfErrors>
  <NumberOfFailures>8</NumberOfFailures>
  <NumberOfIgnoredTests>0</NumberOfIgnoredTests>
  <TotalElapsedTime>00:00:00.004</TotalElapsedTime>
  <DateTimeRan>2019-01-02 22:46:53</DateTimeRan>
</TestResults>

=== output end ===

So if you would please ensure that the testsuite works and maybe integrate your own tests into the TTestInvoke test suite that would be appreciated. :)

silvioprog

2019-01-02 23:24

reporter   ~0113116

Good news to start 2019! \o/ Thanks for checking this issue.

I'm going to download your patch and apply it in my fpc copy to check the tests.rtti.invoke.pas (and merge my tests into it). :-)

(it seems Lazarus from trunk is buggy, but I'll try to revert my copy to an old revision, I urgently need an IDE :-D ...)

silvioprog

2019-01-04 05:46

reporter   ~0113140

Last edited: 2019-01-04 05:49

View 2 revisions

So, I isolated the test "TTestInterfaceClass.Test13" to debug it in a small example, and it worked fine, but using the raw function:

program tests;

{$MODE DELPHI}
{$ASSERTIONS ON}
{$WARN 5079 OFF}

uses
  Math,
  TypInfo,
  Rtti;

type
  ITestIntfRefs = interface(IInvokable)
    function Test(P1: Single; var P2: Single; out P3: Single; constref P4: Single): Single;
  end;

  TTestIntfRefs = class(TInterfacedObject, ITestIntfRefs)
  public
    function Test(P1: Single; var P2: Single; out P3: Single; constref P4: Single): Single;
  end;

function EqF32(A, B: Single): Boolean; inline;
begin
  Result := CompareValue(A, B) = 0;
end;

{ TTestIntfRefs }

function TTestIntfRefs.Test(P1: Single; var P2: Single; out P3: Single;
  constref P4: Single): Single;
begin
  Assert(EqF32(P1, 1.23) and EqF32(P2, 4.56) and EqF32(P4, 7.89));
  P2 := 3.21;
  P3 := 6.54;
  Result := 9.87;
end;

var
  V, V1, V2, V3, V4: TValue;
  F32, F322, F323, F324: Single;
  PF32: PSingle;
  IntfRefs: ITestIntfRefs;
begin
  IntfRefs := TTestIntfRefs.Create;

  TValue.Make(@IntfRefs, TypeInfo(ITestIntfRefs), V);

  F32 := 1.23;
  TValue.Make(@F32, TypeInfo(Single), V1);
  F322 := 4.56;
  PF32 := @F322;
  TValue.Make(@PF32, TypeInfo(PSingle), V2);
  F323 := 0;
  PF32 := @F323;
  TValue.Make(@PF32, TypeInfo(PSingle), V3);
  F324 := 7.89;
  PF32 := @F324;
  TValue.Make(@PF32, TypeInfo(PSingle), V4);

  Assert(EqF32(Rtti.Invoke(@TTestIntfRefs.Test, [V, V1, V2, V3, V4], ccReg,
    TypeInfo(Single), False, False).AsExtended, 9.87));
  Assert(EqF32(F322, 3.21) and EqF32(F323, 6.54));

  WriteLn('All tests OK');
end.


It prints "All tests OK". I can't understand why it fails in the tests.rtti.invoke.pas, since tests are very close. :-/

Could I keep only the "tests.pp"? If so, I can merge the extra tests from "tests.rtti.invoke.pas" to it.

Sven Barth

2019-01-04 15:06

manager   ~0113157

The raw Invoke() and the TRttiMethod/TRttiMethodType/TRttiProcedureType Invoke() call the function call manager with different arguments. E.g. in the latter case the var parameter is TypeInfo(Single) with pfVar set in flags instead of TypeInfo(PSingle) with no flags set. Just to name one example where they are different... (though that doesn't necessarily explain the difference for the result)
I suggest you to add some Writelns to the test and check what exactly fails when it compares the result (I usually comment out all other test functions to avoid too much noise or Writeln only if the method's type is the one I expect).

I don't care what *you* do with tests.pp, but tests.rtti.invoke.pas is the only source for RTTI Invoke related tests in trunk and it will stay like that as long as the RTTI unit is still experimental. And the more tests are added to it the better it will be for others that implement their own function call manager (right now mainly for other CPU targets).

silvioprog

2019-01-05 02:14

reporter   ~0113178

> The raw Invoke() and the TRttiMethod/TRttiMethodType/TRttiProcedureType Invoke() call the function call manager with different arguments. E.g. in the latter case the var parameter is TypeInfo(Single) with pfVar set in flags instead of TypeInfo(PSingle) with no flags set. Just to name one example where they are different...

Perfect! Thanks for explaining. And if I understood, my test (draft) should be changed to:

program Project1;

{$IFDEF FPC}
 {$MODE OBJFPC}
 {$ASSERTIONS ON}
 {$WARN 5079 OFF}
 {$H+}
 {$M+}
{$ENDIF}
{$IFDEF MSWINDOWS}
 {$APPTYPE CONSOLE}
{$ENDIF}

uses
  Math,
  TypInfo,
  Rtti;

type
  ITestIntfRefs = interface(IInvokable)
    function Test(P1: Single; var P2: Single; out P3: Single;
      {$IFDEF FPC}constref{$ELSE}const [Ref]{$ENDIF}P4: Single): Single;
  end;

  TTestIntfRefs = class(TInterfacedObject, ITestIntfRefs)
  public
    function Test(P1: Single; var P2: Single; out P3: Single;
      {$IFDEF FPC}constref{$ELSE}const [Ref]{$ENDIF}P4: Single): Single;
  end;

function EqF32(A, B: Single): Boolean; inline;
begin
  Result := CompareValue(A, B) = 0;
end;

{ TTestIntfRefs }

function TTestIntfRefs.Test(P1: Single; var P2: Single; out P3: Single;
{$IFDEF FPC}constref{$ELSE}const [Ref]{$ENDIF}P4: Single): Single;
begin
  Assert(EqF32(P1, 1.23) and EqF32(P2, 4.56) and EqF32(P4, 7.89));
  P2 := 3.21;
  P3 := 6.54;
  Result := 3.14;
end;

var
  IntfRefs: ITestIntfRefs;
  V, V1, V2, V3, V4, R: TValue;
  F32, F322, F323, F324: Single;
  Ctx: TRttiContext;
  Method: TRttiMethod;
begin
  IntfRefs := TTestIntfRefs.Create;

  TValue.Make(@IntfRefs, TypeInfo(ITestIntfRefs), V);

  F32 := 1.23;
  TValue.Make(@F32, TypeInfo(Single), V1);
  F322 := 4.56;
  TValue.Make(@F322, TypeInfo(Single), V2);
  F323 := 0;
  TValue.Make(@F323, TypeInfo(Single), V3);
  F324 := 7.89;
  TValue.Make(@F324, TypeInfo(Single), V4);

  Ctx := TRttiContext.Create;
  Method := Ctx.GetType(TypeInfo(ITestIntfRefs)).GetMethod('Test');
  R := Method.Invoke(V, [V1, V2, V3, V4]);

  Writeln('V1: ', V1.AsExtended:0:2);
  Writeln('V2: ', V2.AsExtended:0:2);
  Writeln('V3: ', V3.AsExtended:0:2);
  Writeln('V4: ', V4.AsExtended:0:2);
  Writeln('Result: ', R.AsExtended:0:2);

  WriteLn('All tests OK');

{$IFDEF MSWINDOWS}
  ReadLn;
{$ENDIF}
end.


It returns the same result as Delphi:

V1: 1.23
V2: 4.56
V3: 0.00
V4: 7.89
Result: 3.14
All tests OK


Since V2/V3 are not passed as reference the result is the same as the input.

> (though that doesn't necessarily explain the difference for the result)

Indeed. It is very strange, and it worked fine in the isolated example above. :-/

> I suggest you to add some Writelns to the test and check what exactly fails when it compares the result (I usually comment out all other test functions to avoid too much noise or Writeln only if the method's type is the one I expect).

Thanks for suggesting. I'm going to do that.

After your explanations, I agree with you about to use the tests.rtti.invoke.pas as main test. Maybe I need some help to make it works on Linux64 ...

silvioprog

2019-01-05 03:05

reporter   ~0113179

Hm... I found the problem regarding the wrong "result". For the Test13, this change below make it to works:

...

function TTestInterfaceClass.Test13(aArg1: Single; var aArg2: Single; out aArg3: Single; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: Single): Single;
begin
  SetLength(InputArgs, 4);
  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
  aArg2 := SingleArg2Out;
  aArg3 := SingleArg3Out;
  SetLength(OutputArgs, 2);
  TValue.Make(@aArg2, TypeInfo(aArg2), OutputArgs[0]);
  TValue.Make(@aArg3, TypeInfo(aArg3), OutputArgs[1]);
  SetLength(InOutMapping, 2);
  InOutMapping[0] := 1;
  InOutMapping[1] := 2;
  Result := SingleRes;
  TValue.Make(@Result, TypeInfo(Result), ResultValue);
  CalledMethod := 13;

  Result := SingleRes; // *** This line ... ***
end;

...


I'm going to debug it and check why it happens ...

Sven Barth

2019-01-05 17:44

manager   ~0113193

Last edited: 2019-01-05 17:48

View 3 revisions

Regarding the var/out parameters: Your test is wrong. If you want to see result differences for out and var parameters you *must not* use an open array, but a dynamic array variable. See here (I hope I got everything needed, this is from a larger test I have in Delphi):

program Test;

uses
  SysUtils, Rtti;

type
  {$M+}
  ITestInterface = interface
    procedure Test(aArg1: AnsiString; var aArg2: LongInt; out aArg3: LongInt);
  end;
  {$M-}

  TTestClass = class(TInterfacedObject, ITestInterface)
    procedure Test(aArg1: AnsiString; var aArg2: LongInt; out aArg3: LongInt);
  end;

procedure TTestClass.Test(aArg1: AnsiString; var aArg2: LongInt; out aArg3: LongInt);
begin
  Writeln('Test(', aArg1, ', ', aArg2, ', ', aArg3, ')');
  aArg2 := 42;
  aArg3 := 128;
end;

var
  testintf: ITestInterface;
  context: TRttiContext;
  values: array of TValue;
  v: TValue;
  method: TRttiMethod;
  t: TRttiType;
begin
  testintf := TTestClass.Create;

  context := TRttiContext.Create;
  try
    t := context.GetType(TypeInfo(ITestInterface));
    method := t.GetMethod('Test');
    if Assigned(method) then begin
      v := TValue.From(testintf);
      SetLength(values, 3);
      values[0] := TValue.From('Hello World');
      values[1] := TValue.From<LongInt>(1701);
      values[2] := TValue.From<LongInt>(256);
      method.Invoke(v, values); // passed as dynamic array
      for v in values do
        Writeln(v.ToString);
      values[0] := TValue.From('Hello World');
      values[1] := TValue.From<LongInt>(1701);
      values[2] := TValue.From<LongInt>(256);
      v := TValue.From(testintf);
      method.Invoke(v, [values[0], values[1], values[2]]); // passed as open array
      for v in values do
        Writeln(v.ToString);
    end else
      Writeln('Method not found');
  finally
    context.Free;
  end;
end.


This will produce the following result:

Test(Hello World, 1701, 256)
Hello World
42
128
Test(Hello World, 1701, 256)
Hello World
1701
256


The reason is the way how open array parameters are passed compared to a dynamic array parameter (the former is constructed on the stack, the values copied to it and then a pointer passed along; a dynamic array simply gets the pointer to the first element passed along).

This is also why tests.rtti.invoke is constructed the way it is (and at the same time making it easy to add new tests ;) ).

Regarding the Result problem: that is strange cause Result is also set three lines previous and TValue.Make should not change Result in any way :/

silvioprog

2019-01-05 22:02

reporter   ~0113196

Firstly, thanks for sharing the test above. It will help me a lot and I'm going to try it this weekend. :-)

I liked the technique using arrays in the tests. Indeed it will help to add new tests easily.

After trying other tests from tests.rtti.invoke, I found a small bug in my assembly code in the part that handles the function result. My code handled results only for 64/80-bit, today I fixed it to use RAX and RDX and now it handles 128-bit too. After this upgrade more tests passed. :-)

I'll send a new patch as soon as possible ...

Issue History

Date Modified Username Field Change
2018-12-21 08:34 silvioprog New Issue
2018-12-21 08:34 silvioprog File Added: invoke_unix64.inc
2018-12-21 08:36 silvioprog File Added: tests.pp
2019-01-02 23:09 Sven Barth File Added: rtti-sysv-invoke.patch
2019-01-02 23:10 Sven Barth Note Added: 0113114
2019-01-02 23:10 Sven Barth Assigned To => Sven Barth
2019-01-02 23:10 Sven Barth Status new => feedback
2019-01-02 23:11 Sven Barth Note Edited: 0113114 View Revisions
2019-01-02 23:24 silvioprog Note Added: 0113116
2019-01-02 23:24 silvioprog Status feedback => assigned
2019-01-04 05:46 silvioprog Note Added: 0113140
2019-01-04 05:49 silvioprog Note Edited: 0113140 View Revisions
2019-01-04 15:07 Sven Barth Note Added: 0113157
2019-01-05 02:14 silvioprog Note Added: 0113178
2019-01-05 03:05 silvioprog Note Added: 0113179
2019-01-05 17:44 Sven Barth Note Added: 0113193
2019-01-05 17:45 Sven Barth Note Edited: 0113193 View Revisions
2019-01-05 17:48 Sven Barth Note Edited: 0113193 View Revisions
2019-01-05 22:02 silvioprog Note Added: 0113196