[fpc-devel] RTTI for ProcVar types
Steve Hildebrandt
Steve.Kassel at web.de
Sun Mar 24 21:09:23 CET 2013
Am 22.03.2013 13:19, schrieb Sven Barth:
> Am 22.03.2013 12:43, schrieb Steve Hildebrandt:
>> Am 21.03.2013 22:00, schrieb Sven Barth:
>>> On 21.03.2013 15:53, Steve Hildebrandt wrote:
>>>> Am 21.03.2013 11:51, schrieb Sven Barth:
>>>>> Out of curiosity: Why did you add this?
>>>> To implement a less "hacky" way of generic method invokation.
>>>> Supporting several calling conventions I can call a method based on
>>>> the
>>>> address and an array of const as parameters.
>>>> Without RTTI there would've been a need for hard coded meta
>>>> information,
>>>> wich is error prone and rather time consuming.
>>>> Since tkMethod supports RTTI any method contained in a record, class,
>>>> object would work without additional meta information.
>>>
>>> Out of interest: do you support multiple platforms? Maybe if you
>>> want to provide your code under modified LGPL we could use it once
>>> we have the RTTI.TValue type and the extended RTTI to support
>>> RTTI.Invoke.
>> Sure i can provide the code. Some modifications would be needed, so
>> it is easier to adopt it to RTTI.Invoke.
>> Regarding the platforms:
>> Calling conventions 64bit : win64(the microsoft standart thing
>> dunno how its called right now)
>> 32bit : stdcall, register.
>> Since the inline asm code for the actual call differs depending
>> on witch registers are used,
>> this part can't be handled in a generic way and needs to be coded
>> down for each convention.
>> While passing parameters on the Stack works rather generic(well
>> obtaining the stack pointer is the exception here).
>> Calculation of the stack offset/used register too is dependant
>> on the convention.
>> Hope this sums up what needs to be done to port the approach I took
>> to all platforms supported.
> My idea for the extended RTTI was to let the compiler write out all
> necessary information for parameter setting (and also retrieval for
> TVirtualMethodInterceptor support) and then use some kind of mini-meta
> assembly language to generate the final code.
The questing is what is harder to maintain a meta assembly or some asm
snippets doing the calls and managing the stack.
> Regarding the stack pointer:
> http://www.freepascal.org/docs-html/rtl/system/sptr.html
I did look into the sptr function but either I failed to describe what I
need or sptr works in weird ways on win64.
I have the following pice of code the get to the offset of the first
parameter that will be push :
// stackSize includes $20 for the shadow space and $08 for the method
address since the stack should be 16bit aligned
asm (INTEL ASM)
SUB RSP, stackSize
MOV stackPtr, RSP
end;
// Skip shadow space to obtain the offset
stackPtr += $20;
Now if i replace this code with sptr like this :
asm (INTEL ASM)
SUB RSP, stackSize // needs to be done so the called method
obtains a correct value from RSP
//MOV stackPtr, RSP
end;
stackPtr := sptr;
// Skip shadow space to obtain the offset
stackPtr += $20;
I end up with different values while the first one works for my test
cases the second one fails miserably.
Also I think using sptr won't do me much good, since I can't replace all
platform dependant code with operations based one sptr.
(e.g. implementing the skipping of the shadow space, or reserving space
on the stack).
>>>> I'm currently using this to call pascal procedures and use pascal
>>>> classes in LUA.
>>>>> Without you showing what you changed we can not help much...
>>>> typinfo.pp :
>>>> tkMethod, tkProcVar: // simply added tkProcVar here
>>>> (MethodKind : TMethodKind;
>>>> ParamCount : Byte;
>>>> ParamList : array[0..1023] of Char
>>>> ncgrtti.pas:(Line 690)
>>>> procedure procvardef_rtti(def:tprocvardef);
>>>> ...
>>>> begin
>>>> { write method id and name }
>>>> if po_methodpointer in def.procoptions
>>>> then write_header(def,tkMethod)
>>>> else write_header(def,tkProcVar);
>>>> maybe_write_align;
>>>> ...
>>>>
>>>> So that RTTI generation for tkProcVar and tkMethod would only
>>>> differentiate in the TTypeKind field.
>>>
>>> I can reproduce your problem, but I don't have a solution. At least
>>> it is enough to only use that unit, so you could copy the unit
>>> locally and comment everything until the problem disappears. This
>>> way you could pinpoint what construct the problem is and then try to
>>> fix the compiler. And if it then works without further problems we
>>> could add this to trunk.
>> Narrowed it down to those 2 records :
>> type
>> jpeg_entropy_encoder = record
>> //test.lpr(23,1) Error: Undefined symbol: RTTI_$JPEGLIB_$$_DEF2
>> //ORIGINAL JPEGLib Line 539
>> //parameter MCU_data leads to the error
>> encode_mcu : function({cinfo : Pointer; }const MCU_data : array
>> of Pointer) : boolean;
>> end;
>>
>> jpeg_entropy_decoder = record
>> //test.lpr(23,1) Error: Undefined symbol: RTTI_$JPEGLIB_$$_DEF5
>> //ORIGINAL JPEGLib Line 655
>> //parameter MCU_data leads to the error
>> decode_mcu : function({cinfo : Pointer; }var MCU_data : array of
>> Pointer) : boolean;
>> end;
>> But here I'm lost again.
> Seems to be either related to the const/var, the open array (maybe the
> hidden high parameter?) or a combination of both.
>
> You could try to debug the compiler. Simply copy the above records to
> a program unit, load the pp.lpi (or ppcx64.lpi for 64-bit), set up the
> run parameters and the working directory correctly (if you don't have
> a correct configuration for 2.7.1 you should use "-n -Fu..." to find
> the correct RTL units) and then place breakpoints in ncgrtti to check
> whether the correct data is generated.
I fixed the issues I had with the libjpeg. Attached a patch with the
changes I made, or should I file an issue in the bugtracker?
mfg Necem
-------------- next part --------------
Index: compiler/ncgrtti.pas
===================================================================
--- compiler/ncgrtti.pas (revision 23952)
+++ compiler/ncgrtti.pas (working copy)
@@ -687,67 +687,71 @@
methodkind : byte;
i : integer;
begin
- if po_methodpointer in def.procoptions then
- begin
- { write method id and name }
- write_header(def,tkMethod);
- maybe_write_align;
+ { write method id and name }
+ if po_methodpointer in def.procoptions
+ then write_header(def,tkMethod)
+ else
+ begin
+ write_header(def,tkProcVar);
+ if not (def.owner.symtabletype in [ObjectSymtable, staticsymtable,globalsymtable]) then exit;
+ end;
- { write kind of method }
- case def.proctypeoption of
- potype_constructor: methodkind:=mkConstructor;
- potype_destructor: methodkind:=mkDestructor;
- potype_class_constructor: methodkind:=mkClassConstructor;
- potype_class_destructor: methodkind:=mkClassDestructor;
- potype_operator: methodkind:=mkOperatorOverload;
- potype_procedure:
- if po_classmethod in def.procoptions then
- methodkind:=mkClassProcedure
- else
- methodkind:=mkProcedure;
- potype_function:
- if po_classmethod in def.procoptions then
- methodkind:=mkClassFunction
- else
- methodkind:=mkFunction;
- else
- begin
- if def.returndef = voidtype then
- methodkind:=mkProcedure
- else
- methodkind:=mkFunction;
- end;
- end;
- current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(methodkind));
+ maybe_write_align;
- { write parameter info. The parameters must be written in reverse order
- if this method uses right to left parameter pushing! }
- current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(def.maxparacount));
+ { write kind of method }
+ case def.proctypeoption of
+ potype_constructor : methodkind := mkConstructor;
+ potype_destructor : methodkind := mkDestructor;
+ potype_class_constructor : methodkind := mkClassConstructor;
+ potype_class_destructor : methodkind := mkClassDestructor;
+ potype_operator : methodkind := mkOperatorOverload;
+ potype_procedure :
+ begin
+ if po_classmethod in def.procoptions
+ then methodkind := mkClassProcedure
+ else methodkind := mkProcedure;
+ end;
+ potype_function:
+ begin
+ if po_classmethod in def.procoptions
+ then methodkind := mkClassFunction
+ else methodkind := mkFunction;
+ end
+ else
+ begin
+ if def.returndef = voidtype
+ then methodkind := mkProcedure
+ else methodkind := mkFunction;
+ end;
+ end;
- for i:=0 to def.paras.count-1 do
- write_para(tparavarsym(def.paras[i]));
+ current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(methodkind));
- if (methodkind=mkFunction) or (methodkind=mkClassFunction) then
- begin
- { write name of result type }
- write_rtti_name(def.returndef);
- maybe_write_align;
+ { write parameter info. The parameters must be written in reverse order
+ if this method uses right to left parameter pushing! }
+ current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(def.maxparacount));
- { write result typeinfo }
- current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_sym(ref_rtti(def.returndef,fullrtti)))
- end;
+ for i:=0 to def.paras.count-1 do
+ write_para(tparavarsym(def.paras[i]));
- { write calling convention }
- current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(ProcCallOptionToCallConv[def.proccalloption]));
- maybe_write_align;
+ if (methodkind=mkFunction) or (methodkind=mkClassFunction) then
+ begin
+ { write name of result type }
+ write_rtti_name(def.returndef);
+ maybe_write_align;
- { write params typeinfo }
- for i:=0 to def.paras.count-1 do
- if not(vo_is_hidden_para in tparavarsym(def.paras[i]).varoptions) then
- current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_sym(ref_rtti(tparavarsym(def.paras[i]).vardef,fullrtti)));
- end
- else
- write_header(def,tkProcvar);
+ { write result typeinfo }
+ current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_sym(ref_rtti(def.returndef,fullrtti)))
+ end;
+
+ { write calling convention }
+ current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(ProcCallOptionToCallConv[def.proccalloption]));
+ maybe_write_align;
+
+ { write params typeinfo }
+ for i:=0 to def.paras.count-1 do
+ if not(vo_is_hidden_para in tparavarsym(def.paras[i]).varoptions) then
+ current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_sym(ref_rtti(tparavarsym(def.paras[i]).vardef,fullrtti)));
end;
Index: rtl/objpas/typinfo.pp
===================================================================
--- rtl/objpas/typinfo.pp (revision 23952)
+++ rtl/objpas/typinfo.pp (working copy)
@@ -159,7 +159,7 @@
HelperUnit : ShortString
// here the properties follow as array of TPropInfo
);
- tkMethod:
+ tkMethod, tkProcVar:
(MethodKind : TMethodKind;
ParamCount : Byte;
ParamList : array[0..1023] of Char
More information about the fpc-devel
mailing list