diff options
Diffstat (limited to 'compiler/psub.pas')
-rw-r--r-- | compiler/psub.pas | 1475 |
1 files changed, 1475 insertions, 0 deletions
diff --git a/compiler/psub.pas b/compiler/psub.pas new file mode 100644 index 0000000000..28e3fea805 --- /dev/null +++ b/compiler/psub.pas @@ -0,0 +1,1475 @@ +{ + Copyright (c) 1998-2002 by Florian Klaempfl, Daniel Mantione + + Does the parsing and codegeneration at subroutine level + + 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. + + **************************************************************************** +} +unit psub; + +{$i fpcdefs.inc} + +interface + + uses + cclasses,globals, + node,nbas, + symdef,procinfo; + + type + tcgprocinfo = class(tprocinfo) + private + procedure add_entry_exit_code; + public + { code for the subroutine as tree } + code : tnode; + { positions in the tree for init/final } + entry_asmnode, + loadpara_asmnode, + exitlabel_asmnode, + stackcheck_asmnode, + init_asmnode, + final_asmnode : tasmnode; + { list to store the procinfo's of the nested procedures } + nestedprocs : tlinkedlist; + constructor create(aparent:tprocinfo);override; + destructor destroy;override; + procedure printproc; + procedure generate_code; + procedure resetprocdef; + procedure add_to_symtablestack; + procedure remove_from_symtablestack; + procedure parse_body; + end; + + + procedure printnode_reset; + + { reads the declaration blocks } + procedure read_declarations(islibrary : boolean); + + { reads declarations in the interface part of a unit } + procedure read_interface_declarations; + + + +implementation + + uses + { common } + cutils, + { global } + globtype,tokens,verbose,comphook, + systems, + { aasm } + cpubase,aasmbase,aasmtai, + { symtable } + symconst,symbase,symsym,symtype,symtable,defutil, + paramgr, + ppu,fmodule, + { pass 1 } + nutils,nld,ncal,ncon,nflw,nadd,ncnv,nmem, + pass_1, + {$ifdef state_tracking} + nstate, + {$endif state_tracking} + { pass 2 } +{$ifndef NOPASS2} + pass_2, +{$endif} + { parser } + scanner,import,gendef, + pbase,pstatmnt,pdecl,pdecsub,pexports, + { codegen } + tgobj,cgobj,dbgbase, + ncgutil,regvars +{$if defined(arm) or defined(powerpc) or defined(powerpc64)} + ,aasmcpu +{$endif arm} + {$ifndef NOOPT} + {$ifdef i386} + ,aopt386 + {$else i386} + ,aopt + {$endif i386} + {$endif} + ; + +{**************************************************************************** + PROCEDURE/FUNCTION BODY PARSING +****************************************************************************} + + procedure initializevars(p:tnamedindexitem;arg:pointer); + var + b : tblocknode; + begin + if not (tsym(p).typ in [localvarsym,globalvarsym]) then + exit; + with tabstractnormalvarsym(p) do + begin + if assigned(defaultconstsym) then + begin + b:=tblocknode(arg); + b.left:=cstatementnode.create( + cassignmentnode.create( + cloadnode.create(tsym(p),tsym(p).owner), + cloadnode.create(defaultconstsym,defaultconstsym.owner)), + b.left); + end; + end; + end; + + + procedure check_finalize_paras(p : tnamedindexitem;arg:pointer); + begin + if (tsym(p).typ=paravarsym) and + (tparavarsym(p).varspez=vs_value) and + not is_class(tparavarsym(p).vartype.def) and + tparavarsym(p).vartype.def.needs_inittable then + include(current_procinfo.flags,pi_needs_implicit_finally); + end; + + + procedure check_finalize_locals(p : tnamedindexitem;arg:pointer); + begin + if (tsym(p).typ=localvarsym) and + (tlocalvarsym(p).refs>0) and + not(vo_is_funcret in tlocalvarsym(p).varoptions) and + not(is_class(tlocalvarsym(p).vartype.def)) and + tlocalvarsym(p).vartype.def.needs_inittable then + include(current_procinfo.flags,pi_needs_implicit_finally); + end; + + + function block(islibrary : boolean) : tnode; + begin + { parse const,types and vars } + read_declarations(islibrary); + + { do we have an assembler block without the po_assembler? + we should allow this for Delphi compatibility (PFV) } + if (token=_ASM) and (m_delphi in aktmodeswitches) then + include(current_procinfo.procdef.procoptions,po_assembler); + + { Handle assembler block different } + if (po_assembler in current_procinfo.procdef.procoptions) then + begin + block:=assembler_block; + exit; + end; + + {Unit initialization?.} + if ( + assigned(current_procinfo.procdef.localst) and + (current_procinfo.procdef.localst.symtablelevel=main_program_level) and + (current_module.is_unit) + ) or + islibrary then + begin + if (token=_END) then + begin + consume(_END); + { We need at least a node, else the entry/exit code is not + generated and thus no PASCALMAIN symbol which we need (PFV) } + if islibrary then + block:=cnothingnode.create + else + block:=nil; + end + else + begin + if token=_INITIALIZATION then + begin + { The library init code is already called and does not + need to be in the initfinal table (PFV) } + if not islibrary then + current_module.flags:=current_module.flags or uf_init; + block:=statement_block(_INITIALIZATION); + end + else if (token=_FINALIZATION) then + begin + if (current_module.flags and uf_finalize)<>0 then + block:=statement_block(_FINALIZATION) + else + begin + { can we allow no INITIALIZATION for DLL ?? + I think it should work PM } + block:=nil; + exit; + end; + end + else + begin + { The library init code is already called and does not + need to be in the initfinal table (PFV) } + if not islibrary then + current_module.flags:=current_module.flags or uf_init; + block:=statement_block(_BEGIN); + end; + end; + end + else + begin + block:=statement_block(_BEGIN); + if symtablestack.symtabletype=localsymtable then + symtablestack.foreach_static(@initializevars,block); + end; + end; + + +{**************************************************************************** + PROCEDURE/FUNCTION COMPILING +****************************************************************************} + + procedure printnode_reset; + begin + assign(printnodefile,treelogfilename); + {$I-} + rewrite(printnodefile); + {$I+} + if ioresult<>0 then + begin + Comment(V_Error,'Error creating '+treelogfilename); + exit; + end; + close(printnodefile); + end; + + + function generate_bodyentry_block:tnode; + var + srsym : tsym; + para : tcallparanode; + newstatement : tstatementnode; + htype : ttype; + begin + result:=internalstatements(newstatement); + + if assigned(current_procinfo.procdef._class) then + begin + { a constructor needs a help procedure } + if (current_procinfo.procdef.proctypeoption=potype_constructor) then + begin + if is_class(current_procinfo.procdef._class) then + begin + include(current_procinfo.flags,pi_needs_implicit_finally); + srsym:=search_class_member(current_procinfo.procdef._class,'NEWINSTANCE'); + if assigned(srsym) and + (srsym.typ=procsym) then + begin + { if vmt>1 then newinstance } + addstatement(newstatement,cifnode.create( + caddnode.create(gtn, + ctypeconvnode.create_internal( + load_vmt_pointer_node, + voidpointertype), + cpointerconstnode.create(1,voidpointertype)), + cassignmentnode.create( + ctypeconvnode.create_internal( + load_self_pointer_node, + voidpointertype), + ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_vmt_pointer_node,[])), + nil)); + end + else + internalerror(200305108); + end + else + if is_object(current_procinfo.procdef._class) then + begin + htype.setdef(current_procinfo.procdef._class); + htype.setdef(tpointerdef.create(htype)); + { parameter 3 : vmt_offset } + { parameter 2 : address of pointer to vmt, + this is required to allow setting the vmt to -1 to indicate + that memory was allocated } + { parameter 1 : self pointer } + para:=ccallparanode.create( + cordconstnode.create(current_procinfo.procdef._class.vmt_offset,s32inttype,false), + ccallparanode.create( + ctypeconvnode.create_internal( + load_vmt_pointer_node, + voidpointertype), + ccallparanode.create( + ctypeconvnode.create_internal( + load_self_pointer_node, + voidpointertype), + nil))); + addstatement(newstatement,cassignmentnode.create( + ctypeconvnode.create_internal( + load_self_pointer_node, + voidpointertype), + ccallnode.createintern('fpc_help_constructor',para))); + end + else + internalerror(200305103); + { if self=nil then exit + calling fail instead of exit is useless because + there is nothing to dispose (PFV) } + addstatement(newstatement,cifnode.create( + caddnode.create(equaln, + load_self_pointer_node, + cnilnode.create), + cexitnode.create(nil), + nil)); + end; + + { maybe call BeforeDestruction for classes } + if (current_procinfo.procdef.proctypeoption=potype_destructor) and + is_class(current_procinfo.procdef._class) then + begin + srsym:=search_class_member(current_procinfo.procdef._class,'BEFOREDESTRUCTION'); + if assigned(srsym) and + (srsym.typ=procsym) then + begin + { if vmt<>0 then beforedestruction } + addstatement(newstatement,cifnode.create( + caddnode.create(unequaln, + load_vmt_pointer_node, + cnilnode.create), + ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]), + nil)); + end + else + internalerror(200305104); + end; + end; + end; + + + function generate_bodyexit_block:tnode; + var + srsym : tsym; + para : tcallparanode; + newstatement : tstatementnode; + begin + result:=internalstatements(newstatement); + + if assigned(current_procinfo.procdef._class) then + begin + { maybe call AfterConstruction for classes } + if (current_procinfo.procdef.proctypeoption=potype_constructor) and + is_class(current_procinfo.procdef._class) then + begin + srsym:=search_class_member(current_procinfo.procdef._class,'AFTERCONSTRUCTION'); + if assigned(srsym) and + (srsym.typ=procsym) then + begin + { Self can be nil when fail is called } + { if self<>nil and vmt<>nil then afterconstruction } + addstatement(newstatement,cifnode.create( + caddnode.create(andn, + caddnode.create(unequaln, + load_self_pointer_node, + cnilnode.create), + caddnode.create(unequaln, + load_vmt_pointer_node, + cnilnode.create)), + ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]), + nil)); + end + else + internalerror(200305106); + end; + + { a destructor needs a help procedure } + if (current_procinfo.procdef.proctypeoption=potype_destructor) then + begin + if is_class(current_procinfo.procdef._class) then + begin + srsym:=search_class_member(current_procinfo.procdef._class,'FREEINSTANCE'); + if assigned(srsym) and + (srsym.typ=procsym) then + begin + { if self<>0 and vmt=1 then freeinstance } + addstatement(newstatement,cifnode.create( + caddnode.create(andn, + caddnode.create(unequaln, + load_self_pointer_node, + cnilnode.create), + caddnode.create(equaln, + ctypeconvnode.create( + load_vmt_pointer_node, + voidpointertype), + cpointerconstnode.create(1,voidpointertype))), + ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]), + nil)); + end + else + internalerror(200305108); + end + else + if is_object(current_procinfo.procdef._class) then + begin + { finalize object data } + if current_procinfo.procdef._class.needs_inittable then + addstatement(newstatement,finalize_data_node(load_self_node)); + { parameter 3 : vmt_offset } + { parameter 2 : pointer to vmt } + { parameter 1 : self pointer } + para:=ccallparanode.create( + cordconstnode.create(current_procinfo.procdef._class.vmt_offset,s32inttype,false), + ccallparanode.create( + ctypeconvnode.create_internal( + load_vmt_pointer_node, + voidpointertype), + ccallparanode.create( + ctypeconvnode.create_internal( + load_self_pointer_node, + voidpointertype), + nil))); + addstatement(newstatement, + ccallnode.createintern('fpc_help_destructor',para)); + end + else + internalerror(200305105); + end; + end; + end; + + + function generate_except_block:tnode; + var + pd : tprocdef; + newstatement : tstatementnode; + begin + generate_except_block:=internalstatements(newstatement); + + { a constructor needs call destructor (if available) when it + is not inherited } + if assigned(current_procinfo.procdef._class) and + (current_procinfo.procdef.proctypeoption=potype_constructor) then + begin + pd:=current_procinfo.procdef._class.searchdestructor; + if assigned(pd) then + begin + { if vmt<>0 then call destructor } + addstatement(newstatement,cifnode.create( + caddnode.create(unequaln, + load_vmt_pointer_node, + cnilnode.create), + ccallnode.create(nil,tprocsym(pd.procsym),pd.procsym.owner,load_self_node,[]), + nil)); + end; + end + else + begin + { no constructor } + { must be the return value finalized before reraising the exception? } + if (not is_void(current_procinfo.procdef.rettype.def)) and + (current_procinfo.procdef.rettype.def.needs_inittable) and + (not is_class(current_procinfo.procdef.rettype.def)) then + addstatement(newstatement,finalize_data_node(load_result_node)); + end; + end; + + +{**************************************************************************** + TCGProcInfo +****************************************************************************} + + constructor tcgprocinfo.create(aparent:tprocinfo); + begin + inherited Create(aparent); + nestedprocs:=tlinkedlist.create; + end; + + + destructor tcgprocinfo.destroy; + begin + nestedprocs.free; + if assigned(code) then + code.free; + inherited destroy; + end; + + + procedure tcgprocinfo.printproc; + begin + assign(printnodefile,treelogfilename); + {$I-} + append(printnodefile); + if ioresult<>0 then + rewrite(printnodefile); + {$I+} + if ioresult<>0 then + begin + Comment(V_Error,'Error creating '+treelogfilename); + exit; + end; + writeln(printnodefile); + writeln(printnodefile,'*******************************************************************************'); + writeln(printnodefile,procdef.fullprocname(false)); + writeln(printnodefile,'*******************************************************************************'); + printnode(printnodefile,code); + close(printnodefile); + end; + + + procedure tcgprocinfo.add_entry_exit_code; + var + finalcode, + bodyentrycode, + bodyexitcode, + exceptcode : tnode; + newblock : tblocknode; + codestatement, + newstatement : tstatementnode; + oldfilepos : tfileposinfo; + begin + oldfilepos:=aktfilepos; + { Generate code/locations used at start of proc } + aktfilepos:=entrypos; + entry_asmnode:=casmnode.create_get_position; + loadpara_asmnode:=casmnode.create_get_position; + stackcheck_asmnode:=casmnode.create_get_position; + init_asmnode:=casmnode.create_get_position; + bodyentrycode:=generate_bodyentry_block; + { Generate code/locations used at end of proc } + aktfilepos:=exitpos; + exitlabel_asmnode:=casmnode.create_get_position; + final_asmnode:=casmnode.create_get_position; + bodyexitcode:=generate_bodyexit_block; + + { Generate procedure by combining init+body+final, + depending on the implicit finally we need to add + an try...finally...end wrapper } + newblock:=internalstatements(newstatement); + if (cs_implicit_exceptions in aktmoduleswitches) and + (pi_needs_implicit_finally in flags) and + { but it's useless in init/final code of units } + not(procdef.proctypeoption in [potype_unitfinalize,potype_unitinit]) then + begin + { Generate special exception block only needed when + implicit finaly is used } + aktfilepos:=exitpos; + exceptcode:=generate_except_block; + { Generate code that will be in the try...finally } + finalcode:=internalstatements(codestatement); + addstatement(codestatement,bodyexitcode); + addstatement(codestatement,final_asmnode); + { Initialize before try...finally...end frame } + addstatement(newstatement,loadpara_asmnode); + addstatement(newstatement,stackcheck_asmnode); + addstatement(newstatement,entry_asmnode); + addstatement(newstatement,init_asmnode); + addstatement(newstatement,bodyentrycode); + aktfilepos:=entrypos; + addstatement(newstatement,ctryfinallynode.create_implicit( + code, + finalcode, + exceptcode)); + addstatement(newstatement,exitlabel_asmnode); + { set flag the implicit finally has been generated } + include(flags,pi_has_implicit_finally); + end + else + begin + addstatement(newstatement,loadpara_asmnode); + addstatement(newstatement,stackcheck_asmnode); + addstatement(newstatement,entry_asmnode); + addstatement(newstatement,init_asmnode); + addstatement(newstatement,bodyentrycode); + addstatement(newstatement,code); + addstatement(newstatement,exitlabel_asmnode); + addstatement(newstatement,bodyexitcode); + addstatement(newstatement,final_asmnode); + end; + do_firstpass(newblock); + code:=newblock; + aktfilepos:=oldfilepos; + end; + + + procedure clearrefs(p : tnamedindexitem;arg:pointer); + begin + if (tsym(p).typ in [localvarsym,paravarsym,globalvarsym]) then + if tabstractvarsym(p).refs>1 then + tabstractvarsym(p).refs:=1; + end; + + + procedure tcgprocinfo.generate_code; + var + oldprocinfo : tprocinfo; + oldaktmaxfpuregisters : longint; + oldfilepos : tfileposinfo; + templist : Taasmoutput; + headertai : tai; + curralign : longint; + begin + { the initialization procedure can be empty, then we + don't need to generate anything. When it was an empty + procedure there would be at least a blocknode } + if not assigned(code) then + exit; + + { We need valid code } + if Errorcount<>0 then + exit; + + { The RA and Tempgen shall not be available yet } + if assigned(tg) then + internalerror(200309201); + + oldprocinfo:=current_procinfo; + oldfilepos:=aktfilepos; + oldaktmaxfpuregisters:=aktmaxfpuregisters; + + current_procinfo:=self; + aktfilepos:=entrypos; + + { get new labels } + aktbreaklabel:=nil; + aktcontinuelabel:=nil; + templist:=Taasmoutput.create; + + { add parast/localst to symtablestack } + add_to_symtablestack; + + { when size optimization only count occurrence } + if cs_littlesize in aktglobalswitches then + cg.t_times:=1 + else + { reference for repetition is 100 } + cg.t_times:=100; + + { clear register count } + symtablestack.foreach_static(@clearrefs,nil); + symtablestack.next.foreach_static(@clearrefs,nil); + + { there's always a call to FPC_INITIALIZEUNITS/FPC_DO_EXIT in the main program } + if (procdef.localst.symtablelevel=main_program_level) and + (not current_module.is_unit) then + include(flags,pi_do_call); + + { set implicit_finally flag when there are locals/paras to be finalized } + current_procinfo.procdef.parast.foreach_static(@check_finalize_paras,nil); + current_procinfo.procdef.localst.foreach_static(@check_finalize_locals,nil); + + { firstpass everything } + flowcontrol:=[]; + do_firstpass(code); + if code.registersfpu>0 then + include(current_procinfo.flags,pi_uses_fpu); + + { add implicit entry and exit code } + add_entry_exit_code; + + { only do secondpass if there are no errors } + if ErrorCount=0 then + begin + { set the start offset to the start of the temp area in the stack } + tg:=ttgobj.create; + + { Create register allocator } + cg.init_register_allocators; + + set_first_temp_offset; + generate_parameter_info; + + { Allocate space in temp/registers for parast and localst } + aktfilepos:=entrypos; + gen_alloc_symtable(aktproccode,procdef.parast); + gen_alloc_symtable(aktproccode,procdef.localst); + + { Store temp offset for information about 'real' temps } + tempstart:=tg.lasttemp; + + { Generate code to load register parameters in temps and insert local + copies for values parameters. This must be done before the code for the + body is generated because the localloc is updated. + Note: The generated code will be inserted after the code generation of + the body is finished, because only then the position is known } +{$ifdef oldregvars} + assign_regvars(code); +{$endif oldreg} + aktfilepos:=entrypos; + gen_load_para_value(templist); + + { caller paraloc info is also necessary in the stackframe_entry + code of the ppc (and possibly other processors) } + if not procdef.has_paraloc_info then + begin + procdef.requiredargarea:=paramanager.create_paraloc_info(procdef,callerside); + procdef.has_paraloc_info:=true; + end; + + { generate code for the node tree } + do_secondpass(code); + aktproccode.concatlist(exprasmlist); +{$ifdef i386} + procdef.fpu_used:=code.registersfpu; +{$endif i386} + + { The position of the loadpara_asmnode is now known } + aktproccode.insertlistafter(loadpara_asmnode.currenttai,templist); + + { first generate entry and initialize code with the correct + position and switches } + aktfilepos:=entrypos; + aktlocalswitches:=entryswitches; + gen_entry_code(templist); + aktproccode.insertlistafter(entry_asmnode.currenttai,templist); + gen_initialize_code(templist); + aktproccode.insertlistafter(init_asmnode.currenttai,templist); + + { now generate finalize and exit code with the correct position + and switches } + aktfilepos:=exitpos; + aktlocalswitches:=exitswitches; + gen_finalize_code(templist); + { the finalcode must be concated if there was no position available, + using insertlistafter will result in an insert at the start + when currentai=nil } + if assigned(final_asmnode.currenttai) then + aktproccode.insertlistafter(final_asmnode.currenttai,templist) + else + aktproccode.concatlist(templist); + { insert exit label at the correct position } + cg.a_label(templist,aktexitlabel); + if assigned(exitlabel_asmnode.currenttai) then + aktproccode.insertlistafter(exitlabel_asmnode.currenttai,templist) + else + aktproccode.concatlist(templist); + { exit code } + gen_exit_code(templist); + aktproccode.concatlist(templist); + +{$ifdef OLDREGVARS} + { note: this must be done only after as much code as possible has } + { been generated. The result is that when you ungetregister() a } + { regvar, it will actually free the regvar (and alse free the } + { the regvars at the same time). Doing this too early will } + { confuse the register allocator, as the regvars will still be } + { used. It should be done before loading the result regs (so } + { they don't conflict with the regvars) and before } + { gen_entry_code (that one has to be able to allocate the } + { regvars again) (JM) } + free_regvars(aktproccode); +{$endif OLDREGVARS} + + { add code that will load the return value, this is not done + for assembler routines when they didn't reference the result + variable } + gen_load_return_value(templist); + aktproccode.concatlist(templist); + + { generate symbol and save end of header position } + aktfilepos:=entrypos; + gen_proc_symbol(templist); + headertai:=tai(templist.last); + { insert symbol } + aktproccode.insertlist(templist); + + { Free space in temp/registers for parast and localst, must be + done after gen_entry_code } + aktfilepos:=exitpos; + gen_free_symtable(aktproccode,procdef.localst); + gen_free_symtable(aktproccode,procdef.parast); + + { Already reserve all registers for stack checking code and + generate the call to the helper function } + if (cs_check_stack in entryswitches) and + not(po_assembler in procdef.procoptions) and + (current_procinfo.procdef.proctypeoption<>potype_proginit) then + begin + aktfilepos:=entrypos; + gen_stack_check_call(templist); + aktproccode.insertlistafter(stackcheck_asmnode.currenttai,templist) + end; + + { The procedure body is finished, we can now + allocate the registers } + cg.do_register_allocation(aktproccode,headertai); + + { Add save and restore of used registers } + aktfilepos:=entrypos; + gen_save_used_regs(templist); + aktproccode.insertlistafter(headertai,templist); + aktfilepos:=exitpos; + gen_restore_used_regs(aktproccode); + { We know the size of the stack, now we can generate the + parameter that is passed to the stack checking code } + if (cs_check_stack in entryswitches) and + not(po_assembler in procdef.procoptions) and + (current_procinfo.procdef.proctypeoption<>potype_proginit) then + begin + aktfilepos:=entrypos; + gen_stack_check_size_para(templist); + aktproccode.insertlistafter(stackcheck_asmnode.currenttai,templist) + end; + { Add entry code (stack allocation) after header } + aktfilepos:=entrypos; + gen_proc_entry_code(templist); + aktproccode.insertlistafter(headertai,templist); + { Add exit code at the end } + aktfilepos:=exitpos; + gen_proc_exit_code(templist); + aktproccode.concatlist(templist); + + { check if the implicit finally has been generated. The flag + should already be set in pass1 } + if (cs_implicit_exceptions in aktmoduleswitches) and + not(procdef.proctypeoption in [potype_unitfinalize,potype_unitinit]) and + (pi_needs_implicit_finally in flags) and + not(pi_has_implicit_finally in flags) then + internalerror(200405231); + +{$ifndef NoOpt} + if not(cs_no_regalloc in aktglobalswitches) then + begin + if (cs_optimize in aktglobalswitches) and + { do not optimize pure assembler procedures } + not(pi_is_assembler in flags) then + optimize(aktproccode); + end; +{$endif NoOpt} + + { Add end symbol and debug info } + aktfilepos:=exitpos; + gen_proc_symbol_end(templist); + aktproccode.concatlist(templist); + +{$ifdef ARM} + { because of the limited constant size of the arm, all data access is done pc relative } + insertpcrelativedata(aktproccode,aktlocaldata); +{$endif ARM} + +{$ifdef POWERPC} + fixup_jmps(aktproccode); +{$endif POWERPC} +{$ifdef POWERPC64} + fixup_jmps(aktproccode); +{$endif POWERPC64} + { insert line debuginfo } + if (cs_debuginfo in aktmoduleswitches) or + (cs_use_lineinfo in aktglobalswitches) then + debuginfo.insertlineinfo(aktproccode); + + { gprof uses 16 byte granularity } + if (cs_profile in aktmoduleswitches) then + curralign:=16 + else + curralign:=aktalignment.procalign; + + { add the procedure to the al_procedures } + maybe_new_object_file(asmlist[al_procedures]); + new_section(asmlist[al_procedures],sec_code,lower(procdef.mangledname),curralign); + asmlist[al_procedures].concatlist(aktproccode); + { save local data (casetable) also in the same file } + if assigned(aktlocaldata) and + (not aktlocaldata.empty) then + asmlist[al_procedures].concatlist(aktlocaldata); + + { only now we can remove the temps } + tg.resettempgen; + + { stop tempgen and ra } + tg.free; + cg.done_register_allocators; + tg:=nil; + end; + + { restore symtablestack } + remove_from_symtablestack; + + { restore } + templist.free; + aktmaxfpuregisters:=oldaktmaxfpuregisters; + aktfilepos:=oldfilepos; + current_procinfo:=oldprocinfo; + end; + + + procedure tcgprocinfo.add_to_symtablestack; + var + _class,hp : tobjectdef; + begin + { insert symtables for the class, but only if it is no nested function } + if assigned(procdef._class) and + not(assigned(parent) and + assigned(parent.procdef) and + assigned(parent.procdef._class)) then + begin + { insert them in the reverse order } + hp:=nil; + repeat + _class:=procdef._class; + while _class.childof<>hp do + _class:=_class.childof; + hp:=_class; + _class.symtable.next:=symtablestack; + symtablestack:=_class.symtable; + until hp=procdef._class; + end; + + { insert parasymtable in symtablestack when parsing + a function } + if procdef.parast.symtablelevel>=normal_function_level then + begin + procdef.parast.next:=symtablestack; + symtablestack:=procdef.parast; + end; + + procdef.localst.next:=symtablestack; + symtablestack:=procdef.localst; + end; + + + procedure tcgprocinfo.remove_from_symtablestack; + begin + { remove localst/parast } + if procdef.parast.symtablelevel>=normal_function_level then + symtablestack:=symtablestack.next.next + else + symtablestack:=symtablestack.next; + + { remove class member symbol tables } + while symtablestack.symtabletype=objectsymtable do + symtablestack:=symtablestack.next; + end; + + + procedure tcgprocinfo.resetprocdef; + begin + { remove code tree, if not inline procedure } + if assigned(code) then + begin + { the inline procedure has already got a copy of the tree + stored in procdef.inlininginfo } + code.free; + code:=nil; + end; + end; + + + function checknodeinlining(procdef: tprocdef): boolean; + var + i : integer; + currpara : tparavarsym; + begin + result := false; + if (pi_has_assembler_block in current_procinfo.flags) then + exit; + for i:=0 to procdef.paras.count-1 do + begin + currpara:=tparavarsym(procdef.paras[i]); + { we can't handle formaldefs and special arrays (the latter may need a } + { re-basing of the index, i.e. if you pass an array[1..10] as open array, } + { you have to add 1 to all index operations if you directly inline it } + if ((currpara.varspez in [vs_out,vs_var,vs_const]) and + (currpara.vartype.def.deftype=formaldef)) or + is_special_array(currpara.vartype.def) then + exit; + end; + result:=true; + end; + + + procedure tcgprocinfo.parse_body; + var + oldprocinfo : tprocinfo; + oldblock_type : tblock_type; + begin + oldprocinfo:=current_procinfo; + oldblock_type:=block_type; + { reset break and continue labels } + block_type:=bt_body; + + current_procinfo:=self; + + { calculate the lexical level } + if procdef.parast.symtablelevel>maxnesting then + Message(parser_e_too_much_lexlevel); + + { static is also important for local procedures !! } + if (po_staticmethod in procdef.procoptions) then + allow_only_static:=true + else if (procdef.parast.symtablelevel=normal_function_level) then + allow_only_static:=false; + + {$ifdef state_tracking} +{ aktstate:=Tstate_storage.create;} + {$endif state_tracking} + + { create a local symbol table for this routine } + if not assigned(procdef.localst) then + procdef.insert_localst; + + { add parast/localst to symtablestack } + add_to_symtablestack; + + { constant symbols are inserted in this symboltable } + constsymtable:=symtablestack; + + { save entry info } + entrypos:=aktfilepos; + entryswitches:=aktlocalswitches; + + { parse the code ... } + code:=block(current_module.islibrary); + { save exit info } + exitswitches:=aktlocalswitches; + exitpos:=last_endtoken_filepos; + + { the procedure is now defined } + procdef.forwarddef:=false; + + if assigned(code) then + begin + { get a better entry point } + entrypos:=code.fileinfo; + + { Finish type checking pass } + do_resulttypepass(code); + end; + + { Check for unused labels, forwards, symbols for procedures. Static + symtable is checked in pmodules. + The check must be done after the resulttypepass } + if (Errorcount=0) and + (tstoredsymtable(procdef.localst).symtabletype<>staticsymtable) then + begin + { check if forwards are resolved } + tstoredsymtable(procdef.localst).check_forwards; + { check if all labels are used } + tstoredsymtable(procdef.localst).checklabels; + { remove cross unit overloads } + tstoredsymtable(procdef.localst).unchain_overloaded; + { check for unused symbols, but only if there is no asm block } + if not(pi_has_assembler_block in flags) then + begin + tstoredsymtable(procdef.localst).allsymbolsused; + tstoredsymtable(procdef.parast).allsymbolsused; + end; + end; + + if (po_inline in procdef.procoptions) then + begin + { Can we inline this procedure? } + if checknodeinlining(procdef) then + begin + new(procdef.inlininginfo); + include(procdef.procoptions,po_has_inlininginfo); + procdef.inlininginfo^.code:=code.getcopy; + procdef.inlininginfo^.flags:=current_procinfo.flags; + { The blocknode needs to set an exit label } + if procdef.inlininginfo^.code.nodetype=blockn then + include(procdef.inlininginfo^.code.flags,nf_block_with_exit); + end; + end; + + { Print the node to tree.log } + if paraprintnodetree=1 then + printproc; + + { ... remove symbol tables } + remove_from_symtablestack; + + {$ifdef state_tracking} +{ aktstate.destroy;} + {$endif state_tracking} + + { reset to normal non static function } + if (procdef.parast.symtablelevel=normal_function_level) then + allow_only_static:=false; + current_procinfo:=oldprocinfo; + + block_type:=oldblock_type; + end; + + +{**************************************************************************** + PROCEDURE/FUNCTION PARSING +****************************************************************************} + + + procedure check_init_paras(p:tnamedindexitem;arg:pointer); + begin + if tsym(p).typ<>paravarsym then + exit; + with tparavarsym(p) do + if (not is_class(vartype.def) and + vartype.def.needs_inittable and + (varspez in [vs_value,vs_out])) then + include(current_procinfo.flags,pi_do_call); + end; + + + procedure read_proc; + { + Parses the procedure directives, then parses the procedure body, then + generates the code for it + } + + procedure do_generate_code(pi:tcgprocinfo); + var + hpi : tcgprocinfo; + begin + { generate code for this procedure } + pi.generate_code; + { process nested procs } + hpi:=tcgprocinfo(pi.nestedprocs.first); + while assigned(hpi) do + begin + do_generate_code(hpi); + hpi:=tcgprocinfo(hpi.next); + end; + pi.resetprocdef; + end; + + var + old_current_procinfo : tprocinfo; + oldconstsymtable : tsymtable; + oldfailtokenmode : tmodeswitch; + pdflags : tpdflags; + pd : tprocdef; + isnestedproc : boolean; + s : string; + begin + { save old state } + oldconstsymtable:=constsymtable; + old_current_procinfo:=current_procinfo; + + { reset current_procinfo.procdef to nil to be sure that nothing is writing + to an other procdef } + current_procinfo:=nil; + + { parse procedure declaration } + if assigned(old_current_procinfo) and + assigned(old_current_procinfo.procdef) then + pd:=parse_proc_dec(old_current_procinfo.procdef._class) + else + pd:=parse_proc_dec(nil); + + { set the default function options } + if parse_only then + begin + pd.forwarddef:=true; + { set also the interface flag, for better error message when the + implementation doesn't much this header } + pd.interfacedef:=true; + include(pd.procoptions,po_global); + pdflags:=[pd_interface]; + end + else + begin + pdflags:=[pd_body]; + if (not current_module.in_interface) then + include(pdflags,pd_implemen); + if (not current_module.is_unit) or + maybe_smartlink_symbol then + include(pd.procoptions,po_global); + pd.forwarddef:=false; + end; + + { parse the directives that may follow } + parse_proc_directives(pd,pdflags); + + { hint directives, these can be separated by semicolons here, + that needs to be handled here with a loop (PFV) } + while try_consume_hintdirective(pd.symoptions) do + Consume(_SEMICOLON); + + { Set calling convention } + handle_calling_convention(pd); + + { search for forward declarations } + if not proc_add_definition(pd) then + begin + { A method must be forward defined (in the object declaration) } + if assigned(pd._class) and + (not assigned(old_current_procinfo.procdef._class)) then + begin + MessagePos1(pd.fileinfo,parser_e_header_dont_match_any_member,pd.fullprocname(false)); + tprocsym(pd.procsym).write_parameter_lists(pd); + end + else + begin + { Give a better error if there is a forward def in the interface and only + a single implementation } + if (not pd.forwarddef) and + (not pd.interfacedef) and + (tprocsym(pd.procsym).procdef_count>1) and + tprocsym(pd.procsym).first_procdef.forwarddef and + tprocsym(pd.procsym).first_procdef.interfacedef and + not(tprocsym(pd.procsym).procdef_count>2) then + begin + MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,pd.fullprocname(false)); + tprocsym(pd.procsym).write_parameter_lists(pd); + end; + end; + end; + + { Set mangled name } + proc_set_mangledname(pd); + + { compile procedure when a body is needed } + if (pd_body in pdflags) then + begin + Message1(parser_d_procedure_start,pd.fullprocname(false)); + + { create a new procedure } + current_procinfo:=cprocinfo.create(old_current_procinfo); + current_module.procinfo:=current_procinfo; + current_procinfo.procdef:=pd; + isnestedproc:=(current_procinfo.procdef.parast.symtablelevel>normal_function_level); + + { Insert mangledname } + pd.aliasnames.insert(pd.mangledname); + + { Handle Export of this procedure } + if (po_exports in pd.procoptions) and + (target_info.system in [system_i386_os2,system_i386_emx]) then + begin + pd.aliasnames.insert(pd.procsym.realname); + if cs_link_deffile in aktglobalswitches then + deffile.AddExport(pd.mangledname); + end; + + { Insert result variables in the localst } + insert_funcret_local(pd); + + { check if there are para's which require initing -> set } + { pi_do_call (if not yet set) } + if not(pi_do_call in current_procinfo.flags) then + pd.parast.foreach_static(@check_init_paras,nil); + + { set _FAIL as keyword if constructor } + if (pd.proctypeoption=potype_constructor) then + begin + oldfailtokenmode:=tokeninfo^[_FAIL].keyword; + tokeninfo^[_FAIL].keyword:=m_all; + end; + + tcgprocinfo(current_procinfo).parse_body; + + { When it's a nested procedure then defer the code generation, + when back at normal function level then generate the code + for all defered nested procedures and the current procedure } + if isnestedproc then + tcgprocinfo(current_procinfo.parent).nestedprocs.insert(current_procinfo) + else + begin + { We can't support inlining for procedures that have nested + procedures because the nested procedures use a fixed offset + for accessing locals in the parent procedure (PFV) } + if (po_inline in current_procinfo.procdef.procoptions) and + (tcgprocinfo(current_procinfo).nestedprocs.count>0) then + begin + Message1(parser_w_not_supported_for_inline,'nested procedures'); + Message(parser_w_inlining_disabled); + current_procinfo.procdef.proccalloption:=pocall_default; + end; + do_generate_code(tcgprocinfo(current_procinfo)); + end; + + { reset _FAIL as _SELF normal } + if (pd.proctypeoption=potype_constructor) then + tokeninfo^[_FAIL].keyword:=oldfailtokenmode; + + { release procinfo } + if tprocinfo(current_module.procinfo)<>current_procinfo then + internalerror(200304274); + current_module.procinfo:=current_procinfo.parent; + if not isnestedproc then + current_procinfo.free; + + consume(_SEMICOLON); + end + else + begin + { Handle imports } + if (po_external in pd.procoptions) then + begin + { External declared in implementation, and there was already a + forward (or interface) declaration then we need to generate + a stub that calls the external routine } + if (not pd.forwarddef) and + (pd.hasforward) and + not( + assigned(pd.import_dll) and + (target_info.system in [system_i386_win32,system_i386_wdosx, + system_i386_emx,system_i386_os2,system_arm_wince,system_i386_wince]) + ) then + begin + s:=proc_get_importname(pd); + if s<>'' then + gen_external_stub(asmlist[al_procedures],pd,{$IFDEF POWERPC64}'.'+{$ENDIF}s); + end; + + { Import DLL specified? } + if assigned(pd.import_dll) then + begin + { create importlib if not already done } + if not(current_module.uses_imports) then + begin + current_module.uses_imports:=true; + importlib.preparelib(current_module.realmodulename^); + end; + + if assigned(pd.import_name) then + importlib.importprocedure(pd,pd.import_dll^,pd.import_nr,pd.import_name^) + else + importlib.importprocedure(pd,pd.import_dll^,pd.import_nr,''); + end + else + begin + { add import name to external list for DLL scanning } + if target_info.DllScanSupported then + current_module.externals.insert(tExternalsItem.create(proc_get_importname(pd))); + end; + end; + end; + + { Restore old state } + constsymtable:=oldconstsymtable; + + current_procinfo:=old_current_procinfo; + end; + + +{**************************************************************************** + DECLARATION PARSING +****************************************************************************} + + { search in symtablestack for not complete classes } + procedure check_forward_class(p : tnamedindexitem;arg:pointer); + begin + if (tsym(p).typ=typesym) and + (ttypesym(p).restype.def.deftype=objectdef) and + (oo_is_forward in tobjectdef(ttypesym(p).restype.def).objectoptions) then + MessagePos1(tsym(p).fileinfo,sym_e_forward_type_not_resolved,tsym(p).realname); + end; + + + procedure read_declarations(islibrary : boolean); + begin + repeat + if not assigned(current_procinfo) then + internalerror(200304251); + case token of + _LABEL: + label_dec; + _CONST: + const_dec; + _TYPE: + type_dec; + _VAR: + var_dec; + _THREADVAR: + threadvar_dec; + _CONSTRUCTOR, + _DESTRUCTOR, + _FUNCTION, + _PROCEDURE, + _OPERATOR, + _CLASS: + read_proc; + _EXPORTS: + begin + if not(assigned(current_procinfo.procdef.localst)) or + (current_procinfo.procdef.localst.symtablelevel>main_program_level) then + begin + Message(parser_e_syntax_error); + consume_all_until(_SEMICOLON); + end + else if islibrary or + (target_info.system in system_unit_program_exports) then + read_exports + else + begin + Message(parser_w_unsupported_feature); + consume(_BEGIN); + end; + end + else + begin + case idtoken of + _RESOURCESTRING : + begin + { m_class is needed, because the resourcestring + loading is in the ObjPas unit } + if (m_class in aktmodeswitches) then + resourcestring_dec + else + break; + end; + _PROPERTY: + begin + if (m_fpc in aktmodeswitches) then + property_dec + else + break; + end; + else + break; + end; + end; + end; + until false; + + { check for incomplete class definitions, this is only required + for fpc modes } + if (m_fpc in aktmodeswitches) then + symtablestack.foreach_static(@check_forward_class,nil); + end; + + + procedure read_interface_declarations; + begin + repeat + case token of + _CONST : + const_dec; + _TYPE : + type_dec; + _VAR : + var_dec; + _THREADVAR : + threadvar_dec; + _FUNCTION, + _PROCEDURE, + _OPERATOR : + read_proc; + else + begin + case idtoken of + _RESOURCESTRING : + resourcestring_dec; + _PROPERTY: + begin + if (m_fpc in aktmodeswitches) then + property_dec + else + break; + end; + else + break; + end; + end; + end; + until false; + { check for incomplete class definitions, this is only required + for fpc modes } + if (m_fpc in aktmodeswitches) then + symtablestack.foreach_static(@check_forward_class,nil); + end; + + +end. |