| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 | {    Copyright (c) 2008,2015 by Peter Vreman, Florian Klaempfl and Jonas Maebe    This units contains support for generating LLVM type info    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    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.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************}{  This units contains support for LLVM type info generation.  It's based on the debug info system, since it's quite similar}unit llvmtype;{$i fpcdefs.inc}{$h+}interface    uses      cclasses,globtype,      aasmbase,aasmtai,aasmdata,      symbase,symtype,symdef,symsym,      aasmllvm,aasmcnst,      finput,      dbgbase;    { TLLVMTypeInfo }    type      TLLVMTypeInfo = class(TDebugInfo)      protected        { using alias/external declarations it's possible to refer to the same          assembler symbol using multiple types:            function f(p: pointer): pointer; [public, alias: 'FPC_FUNC'];            procedure test(p: pointer); external name 'FPC_FUNC';          We have to insert the appropriate typecasts (per module) for LLVM in          this case. That can only be done after all code for a module has been          generated, as these alias declarations can appear anywhere }        asmsymtypes: THashSet;        procedure record_asmsym_def(sym: TAsmSymbol; def: tdef; redefine: boolean);        function  get_asmsym_def(sym: TAsmSymbol): tdef;        function record_def(def:tdef): tdef;        procedure appenddef_array(list:TAsmList;def:tarraydef);override;        procedure appenddef_abstractrecord(list:TAsmList;def:tabstractrecorddef);        procedure appenddef_record(list:TAsmList;def:trecorddef);override;        procedure appenddef_pointer(list:TAsmList;def:tpointerdef);override;        procedure appenddef_procvar(list:TAsmList;def:tprocvardef);override;        procedure appendprocdef(list:TAsmList;def:tprocdef);override;        procedure appenddef_object(list:TAsmList;def: tobjectdef);override;        procedure appenddef_classref(list: TAsmList; def: tclassrefdef);override;        procedure appenddef_variant(list:TAsmList;def: tvariantdef);override;        procedure appenddef_file(list:TasmList;def:tfiledef);override;        procedure appendsym_var(list:TAsmList;sym:tabstractnormalvarsym);        procedure appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);override;        procedure appendsym_paravar(list:TAsmList;sym:tparavarsym);override;        procedure appendsym_localvar(list:TAsmList;sym:tlocalvarsym);override;        procedure appendsym_fieldvar(list:TAsmList;sym:tfieldvarsym);override;        procedure appendsym_const(list:TAsmList;sym:tconstsym);override;        procedure appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);override;        procedure enum_membersyms_callback(p:TObject;arg:pointer);        procedure collect_llvmins_info(deftypelist: tasmlist; p: taillvm);        procedure collect_tai_info(deftypelist: tasmlist; p: tai);        procedure collect_asmlist_info(deftypelist, asmlist: tasmlist);        procedure insert_llvmins_typeconversions(toplevellist: tasmlist; p: taillvm);        procedure insert_typedconst_typeconversion(toplevellist: tasmlist; p: tai_abstracttypedconst);        procedure insert_tai_typeconversions(toplevellist: tasmlist; p: tai);        procedure insert_asmlist_typeconversions(toplevellist, list: tasmlist);        procedure maybe_insert_extern_sym_decl(toplevellist: tasmlist; sym: tasmsymbol; def: tdef);        procedure update_asmlist_alias_types(list: tasmlist);      public        constructor Create;override;        destructor Destroy;override;        procedure inserttypeinfo;override;      end;implementation    uses      sysutils,cutils,cfileutl,constexp,      version,globals,verbose,systems,      cpubase,cgbase,paramgr,      fmodule,nobj,      defutil,defcmp,symconst,symtable,      llvmbase,llvmdef      ;{****************************************************************************                              TDebugInfoDwarf****************************************************************************}    procedure TLLVMTypeInfo.record_asmsym_def(sym: TAsmSymbol; def: tdef; redefine: boolean);      var        res: PHashSetItem;      begin        record_def(def);        res:=asmsymtypes.FindOrAdd(@sym,sizeof(sym));        { due to internal aliases with different signatures, we may end up with          multiple defs for the same symbol -> use the one from the declaration,          and insert typecasts as necessary elsewhere }        if redefine or           not assigned(res^.Data) then          res^.Data:=def;      end;    function TLLVMTypeInfo.get_asmsym_def(sym: TAsmSymbol): tdef;      var        res: PHashSetItem;      begin        res:=asmsymtypes.Find(@sym,sizeof(sym));        { we must have a def for every used asmsym }        if not assigned(res) or           not assigned(res^.data) then          internalerror(2015042701);        result:=tdef(res^.Data);      end;    function TLLVMTypeInfo.record_def(def:tdef): tdef;      begin        result:=def;        if def.stab_number<>0 then          exit;        def.stab_number:=1;        if def.dbg_state=dbg_state_unused then          begin            def.dbg_state:=dbg_state_used;            deftowritelist.Add(def);          end;        defnumberlist.Add(def);      end;    constructor TLLVMTypeInfo.Create;      begin        inherited Create;        asmsymtypes:=THashSet.Create(current_asmdata.AsmSymbolDict.Count,true,false);      end;    destructor TLLVMTypeInfo.Destroy;      begin        asmsymtypes.free;        inherited destroy;      end;    procedure TLLVMTypeInfo.enum_membersyms_callback(p:TObject; arg: pointer);      begin        case tsym(p).typ of          fieldvarsym:            appendsym_fieldvar(TAsmList(arg),tfieldvarsym(p));        end;      end;    procedure TLLVMTypeInfo.collect_llvmins_info(deftypelist: tasmlist; p: taillvm);      var        opidx, paraidx: longint;        callpara: pllvmcallpara;      begin        for opidx:=0 to p.ops-1 do          case p.oper[opidx]^.typ of            top_def:              record_def(p.oper[opidx]^.def);            top_tai:              collect_tai_info(deftypelist,p.oper[opidx]^.ai);            top_ref:              begin                if (p.llvmopcode<>la_br) and                   assigned(p.oper[opidx]^.ref^.symbol) and                   (p.oper[opidx]^.ref^.symbol.bind<>AB_TEMP) then                  begin                    if (opidx=3) and                       (p.llvmopcode=la_call) then                      record_asmsym_def(p.oper[opidx]^.ref^.symbol,tpointerdef(p.oper[2]^.def).pointeddef,false)                    { not a named register }                    else if (p.oper[opidx]^.ref^.refaddr<>addr_full) then                      record_asmsym_def(p.oper[opidx]^.ref^.symbol,p.spilling_get_reg_type(opidx),false);                  end;              end;            top_para:              for paraidx:=0 to p.oper[opidx]^.paras.count-1 do                begin                  callpara:=pllvmcallpara(p.oper[opidx]^.paras[paraidx]);                  record_def(callpara^.def);                end;          end;      end;    procedure TLLVMTypeInfo.collect_tai_info(deftypelist: tasmlist; p: tai);      var        value: tai_abstracttypedconst;      begin        if not assigned(p) then          exit;        case p.typ of          ait_llvmalias:            begin              record_asmsym_def(taillvmalias(p).newsym,taillvmalias(p).def,true);            end;          ait_llvmdecl:            begin              record_asmsym_def(taillvmdecl(p).namesym,taillvmdecl(p).def,true);              collect_asmlist_info(deftypelist,taillvmdecl(p).initdata);            end;          ait_llvmins:            collect_llvmins_info(deftypelist,taillvm(p));          ait_typedconst:            begin              record_def(tai_abstracttypedconst(p).def);              case tai_abstracttypedconst(p).adetyp of                tck_simple:                  collect_tai_info(deftypelist,tai_simpletypedconst(p).val);                tck_array,tck_record:                  for value in tai_aggregatetypedconst(p) do                    collect_tai_info(deftypelist,value);              end;            end;        end;      end;    procedure TLLVMTypeInfo.collect_asmlist_info(deftypelist, asmlist: tasmlist);      var        hp: tai;      begin        if not assigned(asmlist) then          exit;        hp:=tai(asmlist.first);        while assigned(hp) do          begin            collect_tai_info(deftypelist,hp);            hp:=tai(hp.next);          end;      end;    function equal_llvm_defs(def1, def2: tdef): boolean;      var        def1str, def2str: TSymStr;      begin        if def1=def2 then          exit(true);        def1str:=llvmencodetypename(def1);        def2str:=llvmencodetypename(def2);        { normalise both type representations in case one is a procdef          and the other is a procvardef}        if def1.typ=procdef then          def1str:=def1str+'*';        if def2.typ=procdef then          def2str:=def2str+'*';        result:=def1str=def2str;      end;    procedure TLLVMTypeInfo.insert_llvmins_typeconversions(toplevellist: tasmlist; p: taillvm);      var        symdef,        opdef,        opcmpdef: tdef;        cnv: taillvm;        i: longint;      begin        case p.llvmopcode of          la_call:            if p.oper[3]^.typ=top_ref then              begin                maybe_insert_extern_sym_decl(toplevellist,p.oper[3]^.ref^.symbol,tpointerdef(p.oper[2]^.def).pointeddef);                symdef:=get_asmsym_def(p.oper[3]^.ref^.symbol);                { the type used in the call is different from the type used to                  declare the symbol -> insert a typecast }                if not equal_llvm_defs(symdef,p.oper[2]^.def) then                  begin                    if symdef.typ=procdef then                      { ugly, but can't use getcopyas(procvardef) due to the                        symtablestack not being available here (cpointerdef.getreusable                        is hardcoded to put things in the current module's                        symtable) and "pointer to procedure" results in the                        correct llvm type }                      symdef:=cpointerdef.getreusable(tprocdef(symdef));                    cnv:=taillvm.op_reg_size_sym_size(la_bitcast,NR_NO,symdef,p.oper[3]^.ref^.symbol,p.oper[2]^.def);                    p.loadtai(3,cnv);                  end;              end;          else if p.llvmopcode<>la_br then            begin              { check the types of all symbolic operands }              for i:=0 to p.ops-1 do                case p.oper[i]^.typ of                  top_ref:                    if (p.oper[i]^.ref^.refaddr<>addr_full) and                       assigned(p.oper[i]^.ref^.symbol) and                       (p.oper[i]^.ref^.symbol.bind<>AB_TEMP) then                      begin                        opdef:=p.spilling_get_reg_type(i);                        case opdef.typ of                          pointerdef:                            opcmpdef:=tpointerdef(opdef).pointeddef;                          procvardef,                          procdef:                            opcmpdef:=opdef;                          else                            internalerror(2015073101);                        end;                        maybe_insert_extern_sym_decl(toplevellist,p.oper[i]^.ref^.symbol,opcmpdef);                        symdef:=get_asmsym_def(p.oper[i]^.ref^.symbol);                        if not equal_llvm_defs(symdef,opcmpdef) then                          begin                            if symdef.typ=procdef then                              symdef:=cpointerdef.getreusable(symdef);                            cnv:=taillvm.op_reg_size_sym_size(la_bitcast,NR_NO,cpointerdef.getreusable(symdef),p.oper[i]^.ref^.symbol,opdef);                            p.loadtai(i,cnv);                          end;                      end;                  top_tai:                    insert_tai_typeconversions(toplevellist,p.oper[i]^.ai);                end;            end;        end;      end;    procedure TLLVMTypeInfo.insert_typedconst_typeconversion(toplevellist: tasmlist; p: tai_abstracttypedconst);      var        symdef: tdef;        cnv: taillvm;        elementp: tai_abstracttypedconst;      begin        case p.adetyp of          tck_simple:            begin              case tai_simpletypedconst(p).val.typ of                ait_const:                  if assigned(tai_const(tai_simpletypedconst(p).val).sym) and                     not assigned(tai_const(tai_simpletypedconst(p).val).endsym) then                    begin                      maybe_insert_extern_sym_decl(toplevellist,tai_const(tai_simpletypedconst(p).val).sym,p.def);                      symdef:=get_asmsym_def(tai_const(tai_simpletypedconst(p).val).sym);                      { all references to symbols in typed constants are                        references to the address of a global symbol (you can't                        refer to the data itself, just like you can't initialise                        a Pascal (typed) constant with the contents of another                        typed constant) }                      symdef:=cpointerdef.getreusable(symdef);                      if not equal_llvm_defs(symdef,p.def) then                        begin                          cnv:=taillvm.op_reg_tai_size(la_bitcast,NR_NO,tai_simpletypedconst.create(tck_simple,symdef,tai_simpletypedconst(p).val),p.def);                          tai_simpletypedconst(p).val:=cnv;                        end;                    end;                else                  insert_tai_typeconversions(toplevellist,tai_simpletypedconst(p).val);              end;            end;          tck_array,          tck_record:            begin              for elementp in tai_aggregatetypedconst(p) do                insert_typedconst_typeconversion(toplevellist,elementp);            end;        end;      end;    procedure TLLVMTypeInfo.insert_tai_typeconversions(toplevellist: tasmlist; p: tai);      begin        if not assigned(p) then          exit;        case p.typ of          ait_llvmins:            insert_llvmins_typeconversions(toplevellist,taillvm(p));          { can also be necessary in case someone initialises a typed const with            the address of an external symbol aliasing one declared with a            different type in the same mmodule. }          ait_typedconst:            insert_typedconst_typeconversion(toplevellist,tai_abstracttypedconst(p));          ait_llvmdecl:            insert_asmlist_typeconversions(toplevellist,taillvmdecl(p).initdata);        end;      end;    procedure TLLVMTypeInfo.insert_asmlist_typeconversions(toplevellist, list: tasmlist);      var        hp: tai;      begin        if not assigned(list) then          exit;        hp:=tai(list.first);        while assigned(hp) do          begin            insert_tai_typeconversions(toplevellist,hp);            hp:=tai(hp.next);          end;      end;    procedure TLLVMTypeInfo.maybe_insert_extern_sym_decl(toplevellist: tasmlist; sym: tasmsymbol; def: tdef);      var        sec: tasmsectiontype;      begin        { Necessery for "external" declarations for symbols not declared in the          current unit. We can't create these declarations when the alias is          initially generated, because the symbol may still be defined later at          that point.          We also do it for all other external symbol references (e.g.          references to symbols declared in other units), because then this          handling is centralised in one place. }        if not(sym.declared) then          begin            if def.typ=procdef then              sec:=sec_code            else              sec:=sec_data;            toplevellist.Concat(taillvmdecl.createdecl(sym,def,nil,sec,def.alignment));            record_asmsym_def(sym,def,true);          end;      end;    procedure TLLVMTypeInfo.update_asmlist_alias_types(list: tasmlist);      var        hp: tai;        def: tdef;      begin        if not assigned(list) then          exit;        hp:=tai(list.first);        while assigned(hp) do          begin            case hp.typ of              ait_llvmalias:                begin                  { replace the def of the alias declaration with the def of                    the aliased symbol -> we'll insert the appropriate type                    conversions for all uses of this symbol in the code (since                    every use also specifies the used type) }                  record_asmsym_def(taillvmalias(hp).oldsym,taillvmalias(hp).def,false);                  def:=get_asmsym_def(taillvmalias(hp).oldsym);                  if taillvmalias(hp).def<>def then                    begin                      taillvmalias(hp).def:=def;                      record_asmsym_def(taillvmalias(hp).newsym,def,true);                    end;                end;              ait_llvmdecl:                update_asmlist_alias_types(taillvmdecl(hp).initdata);            end;            hp:=tai(hp.next);          end;      end;    procedure TLLVMTypeInfo.appenddef_array(list:TAsmList;def:tarraydef);      begin        record_def(def);        appenddef(list,def.elementdef);      end;    procedure TLLVMTypeInfo.appenddef_abstractrecord(list:TAsmList;def:tabstractrecorddef);      var        symdeflist: tfpobjectlist;        i: longint;      begin        record_def(def);        symdeflist:=tabstractrecordsymtable(def.symtable).llvmst.symdeflist;        for i:=0 to symdeflist.Count-1 do          record_def(tllvmshadowsymtableentry(symdeflist[i]).def);        if assigned(def.typesym) then          list.concat(taillvm.op_size(LA_TYPE,record_def(def)));      end;    procedure TLLVMTypeInfo.appenddef_record(list:TAsmList;def:trecorddef);      begin        appenddef_abstractrecord(list,def);      end;    procedure TLLVMTypeInfo.appenddef_pointer(list:TAsmList;def:tpointerdef);      begin        record_def(def);        appenddef(list,def.pointeddef);      end;    procedure TLLVMTypeInfo.appenddef_procvar(list:TAsmList;def:tprocvardef);      var        i: longint;      begin        record_def(def);        { todo: handle mantis #25551; there is no way to create a symbolic          la_type for a procvardef (unless it's a procedure of object/record),          which means that recursive references should become plain "procedure"          types that are then casted to the real type when they are used }        def.init_paraloc_info(callerside);        for i:=0 to def.paras.count-1 do          appenddef(list,llvmgetcgparadef(tparavarsym(def.paras[i]).paraloc[callerside],true));        appenddef(list,llvmgetcgparadef(def.funcretloc[callerside],true));        if assigned(def.typesym) and           not def.is_addressonly then          list.concat(taillvm.op_size(LA_TYPE,record_def(def)));      end;    procedure TLLVMTypeInfo.appendprocdef(list:TAsmList;def:tprocdef);      begin        { the procdef itself is already written by appendprocdef_implicit }              { last write the types from this procdef }        if assigned(def.parast) then          write_symtable_defs(current_asmdata.asmlists[al_start],def.parast);        if assigned(def.localst) and           (def.localst.symtabletype=localsymtable) then          write_symtable_defs(current_asmdata.asmlists[al_start],def.localst);      end;    procedure TLLVMTypeInfo.appendsym_var(list:TAsmList;sym:tabstractnormalvarsym);      begin        appenddef(list,sym.vardef);      end;    procedure TLLVMTypeInfo.appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);      begin        appendsym_var(list,sym);      end;    procedure TLLVMTypeInfo.appendsym_localvar(list:TAsmList;sym:tlocalvarsym);      begin        appendsym_var(list,sym);      end;    procedure TLLVMTypeInfo.appendsym_paravar(list:TAsmList;sym:tparavarsym);      begin        appendsym_var(list,sym);      end;    procedure TLLVMTypeInfo.appendsym_fieldvar(list:TAsmList;sym: tfieldvarsym);      begin        appenddef(list,sym.vardef);      end;    procedure TLLVMTypeInfo.appendsym_const(list:TAsmList;sym:tconstsym);      begin        appenddef(list,sym.constdef);      end;    procedure TLLVMTypeInfo.appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);      begin        appenddef(list,sym.vardef);      end;    procedure TLLVMTypeInfo.inserttypeinfo;      procedure write_defs_to_write;        var          n       : integer;          looplist,          templist: TFPObjectList;          def     : tdef;        begin          templist := TFPObjectList.Create(False);          looplist := deftowritelist;          while looplist.count > 0 do            begin              deftowritelist := templist;              for n := 0 to looplist.count - 1 do                begin                  def := tdef(looplist[n]);                  case def.dbg_state of                    dbg_state_written:                      continue;                    dbg_state_writing:                      internalerror(200610052);                    dbg_state_unused:                      internalerror(200610053);                    dbg_state_used:                      appenddef(current_asmdata.asmlists[al_start],def)                  else                    internalerror(200610054);                  end;                end;              looplist.clear;              templist := looplist;              looplist := deftowritelist;            end;          templist.free;        end;      var        storefilepos: tfileposinfo;        def: tdef;        i: longint;        hal: tasmlisttype;      begin        if cs_no_regalloc in current_settings.globalswitches then          exit;        storefilepos:=current_filepos;        current_filepos:=current_module.mainfilepos;        defnumberlist:=TFPObjectList.create(false);        deftowritelist:=TFPObjectList.create(false);        { write all global/static variables, part of flaggin all required tdefs  }        if assigned(current_module.globalsymtable) then          write_symtable_syms(current_asmdata.asmlists[al_start],current_module.globalsymtable);        if assigned(current_module.localsymtable) then          write_symtable_syms(current_asmdata.asmlists[al_start],current_module.localsymtable);        { write all procedures and methods, part of flagging all required tdefs }        if assigned(current_module.globalsymtable) then          write_symtable_procdefs(current_asmdata.asmlists[al_start],current_module.globalsymtable);        if assigned(current_module.localsymtable) then          write_symtable_procdefs(current_asmdata.asmlists[al_start],current_module.localsymtable);        { process all llvm instructions, part of flagging all required tdefs }        for hal:=low(TasmlistType) to high(TasmlistType) do          if hal<>al_start then            collect_asmlist_info(current_asmdata.asmlists[al_start],current_asmdata.asmlists[hal]);        { update the defs of all alias declarations so they match those of the          declarations of the symbols they alias }        for hal:=low(TasmlistType) to high(TasmlistType) do          if hal<>al_start then            update_asmlist_alias_types(current_asmdata.asmlists[hal]);        { and insert the necessary type conversions }        for hal:=low(TasmlistType) to high(TasmlistType) do          if hal<>al_start then            insert_asmlist_typeconversions(              current_asmdata.asmlists[hal],              current_asmdata.asmlists[hal]);        { write all used defs }        write_defs_to_write;        { reset all def labels }        for i:=0 to defnumberlist.count-1 do          begin            def:=tdef(defnumberlist[i]);            def.dbg_state:=dbg_state_unused;            def.stab_number:=0;          end;        defnumberlist.free;        defnumberlist:=nil;        deftowritelist.free;        deftowritelist:=nil;        current_filepos:=storefilepos;      end;    procedure TLLVMTypeInfo.appenddef_object(list:TAsmList;def: tobjectdef);      begin        if is_interface(def) then          begin            record_def(def);            record_def(def.vmt_def);          end        else          appenddef_abstractrecord(list,def);      end;    procedure TLLVMTypeInfo.appenddef_classref(list: TAsmList; def: tclassrefdef);      begin        record_def(def);        { can also be an objcclass, which doesn't have a vmt }        if is_class(tclassrefdef(def).pointeddef) then          record_def(tobjectdef(tclassrefdef(def).pointeddef).vmt_def);      end;    procedure TLLVMTypeInfo.appenddef_variant(list:TAsmList;def: tvariantdef);      begin        record_def(def);        appenddef(list,tabstractrecorddef(search_system_type('TVARDATA').typedef));      end;    procedure TLLVMTypeInfo.appenddef_file(list:TAsmList;def:tfiledef);      begin        record_def(def);        case tfiledef(def).filetyp of          ft_text    :            appenddef(list,tabstractrecorddef(search_system_type('TEXTREC').typedef));          ft_typed,          ft_untyped :            appenddef(list,tabstractrecorddef(search_system_type('FILEREC').typedef));        end;      end;end.
 |