{ Copyright (c) 1998-2002 by Florian Klaempfl Reads typed constants 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 ptconst; {$i fpcdefs.inc} interface uses symtype,symsym; { this procedure reads typed constants } { sym is only needed for ansi strings } { the assembler label is in the middle (PM) } procedure readtypedconst(const t:ttype;sym : ttypedconstsym;writable : boolean); implementation uses strings, globtype,systems,tokens,verbose, cutils,globals,widestr,scanner, symconst,symbase,symdef,symtable, aasmbase,aasmtai,aasmcpu,defutil,defcmp, { pass 1 } node,htypechk,procinfo, nmat,nadd,ncal,nmem,nset,ncnv,ninl,ncon,nld,nflw, { parser specific stuff } pbase,pexpr, { codegen } cpuinfo,cgbase,dbgbase ; {$ifdef fpc} {$maxfpuregisters 0} {$endif fpc} { this procedure reads typed constants } procedure readtypedconst(const t:ttype;sym : ttypedconstsym;writable : boolean); label myexit; type setbytes = array[0..31] of byte; Psetbytes = ^setbytes; var len,base : longint; p,hp : tnode; i,j,l, varalign : longint; offset, strlength : aint; ll : tasmlabel; s,sorg : string; c : char; ca : pchar; tmpguid : tguid; aktpos : longint; obj : tobjectdef; recsym, srsym : tsym; symt : tsymtable; value : bestreal; intvalue : tconstexprint; strval : pchar; pw : pcompilerwidestring; error : boolean; old_block_type : tblock_type; storefilepos : tfileposinfo; cursectype : TAsmSectionType; cural : tasmlist; procedure check_range(def:torddef); begin if ((tordconstnode(p).value>def.high) or (tordconstnode(p).valueu32bit then check_range(torddef(t.def)); end else Message(parser_e_illegal_expression); end; s64bit, u64bit, scurrency: begin if is_constintnode(p) then intvalue := tordconstnode(p).value else if is_constrealnode(p) and (torddef(t.def).typ=scurrency) { allow bootstrapping } then begin intvalue:=round(trealconstnode(p).value_real*10000); end else begin intvalue:=0; Message(parser_e_illegal_expression); end; asmlist[cural].concat(Tai_const.Create_64bit(intvalue)); end; else internalerror(3799); end; p.free; end; floatdef: begin p:=comp_expr(true); if is_constrealnode(p) then value:=trealconstnode(p).value_real else if is_constintnode(p) then value:=tordconstnode(p).value else Message(parser_e_illegal_expression); case tfloatdef(t.def).typ of s32real : asmlist[cural].concat(Tai_real_32bit.Create(ts32real(value))); s64real : {$ifdef ARM} if aktfputype in [fpu_fpa,fpu_fpa10,fpu_fpa11] then asmlist[cural].concat(Tai_real_64bit.Create_hiloswapped(ts64real(value))) else {$endif ARM} asmlist[cural].concat(Tai_real_64bit.Create(ts64real(value))); s80real : asmlist[cural].concat(Tai_real_80bit.Create(value)); { the round is necessary for native compilers where comp isn't a float } s64comp : asmlist[cural].concat(Tai_comp_64bit.Create(round(value))); s64currency: asmlist[cural].concat(Tai_comp_64bit.Create(round(value*10000))); s128real: asmlist[cural].concat(Tai_real_128bit.Create(value)); else internalerror(18); end; p.free; end; classrefdef: begin p:=comp_expr(true); case p.nodetype of loadvmtaddrn: with Tclassrefdef(p.resulttype.def) do begin if not Tobjectdef(pointertype.def).is_related(Tobjectdef(pointertype.def)) then message(parser_e_illegal_expression); asmlist[cural].concat(Tai_const.Create_sym(objectlibrary.newasmsymbol( Tobjectdef(pointertype.def).vmt_mangledname,AB_EXTERNAL,AT_DATA))); end; niln: asmlist[cural].concat(Tai_const.Create_sym(nil)); else Message(parser_e_illegal_expression); end; p.free; end; pointerdef: begin p:=comp_expr(true); if (p.nodetype=typeconvn) then with Ttypeconvnode(p) do if (left.nodetype in [addrn,niln]) and equal_defs(t.def,p.resulttype.def) then begin hp:=left; left:=nil; p.free; p:=hp; end; { allows horrible ofs(typeof(TButton)^) code !! } if (p.nodetype=addrn) then with Taddrnode(p) do if left.nodetype=derefn then begin hp:=tderefnode(left).left; tderefnode(left).left:=nil; p.free; p:=hp; end; { const pointer ? } if (p.nodetype = pointerconstn) then begin if sizeof(TConstPtrUInt)=8 then asmlist[cural].concat(Tai_const.Create_64bit(TConstPtrUInt(tpointerconstnode(p).value))) else if sizeof(TConstPtrUInt)=4 then asmlist[cural].concat(Tai_const.Create_32bit(TConstPtrUInt(tpointerconstnode(p).value))) else internalerror(200404122); end { nil pointer ? } else if p.nodetype=niln then asmlist[cural].concat(Tai_const.Create_sym(nil)) { maybe pchar ? } else if is_char(tpointerdef(t.def).pointertype.def) and (p.nodetype<>addrn) then begin objectlibrary.getdatalabel(ll); asmlist[cural].concat(Tai_const.Create_sym(ll)); if p.nodetype=stringconstn then varalign:=size_2_align(tstringconstnode(p).len) else varalign:=0; varalign:=const_align(varalign); asmlist[al_const].concat(Tai_align.Create(varalign)); asmlist[al_const].concat(Tai_label.Create(ll)); if p.nodetype=stringconstn then begin len:=tstringconstnode(p).len; { For tp7 the maximum lentgh can be 255 } if (m_tp7 in aktmodeswitches) and (len>255) then len:=255; getmem(ca,len+2); move(tstringconstnode(p).value_str^,ca^,len+1); asmlist[al_const].concat(Tai_string.Create_pchar(ca,len+1)); end else if is_constcharnode(p) then asmlist[al_const].concat(Tai_string.Create(char(byte(tordconstnode(p).value))+#0)) else message(parser_e_illegal_expression); end { maybe pwidechar ? } else if is_widechar(tpointerdef(t.def).pointertype.def) and (p.nodetype<>addrn) then begin objectlibrary.getdatalabel(ll); asmlist[cural].concat(Tai_const.Create_sym(ll)); asmlist[al_typedconsts].concat(tai_align.create(const_align(sizeof(aint)))); asmlist[al_typedconsts].concat(Tai_label.Create(ll)); if (p.nodetype in [stringconstn,ordconstn]) then begin { convert to widestring stringconstn } inserttypeconv(p,cwidestringtype); if (p.nodetype=stringconstn) and (tstringconstnode(p).st_type=st_widestring) then begin pw:=pcompilerwidestring(tstringconstnode(p).value_str); for i:=0 to tstringconstnode(p).len-1 do asmlist[al_typedconsts].concat(Tai_const.Create_16bit(pw^.data[i])); { ending #0 } asmlist[al_typedconsts].concat(Tai_const.Create_16bit(0)) end; end else Message(parser_e_illegal_expression); end else if (p.nodetype=addrn) or is_procvar_load(p) then begin { insert typeconv } inserttypeconv(p,t); hp:=p; while assigned(hp) and (hp.nodetype in [addrn,typeconvn,subscriptn,vecn]) do hp:=tunarynode(hp).left; if (hp.nodetype=loadn) then begin hp:=p; offset:=0; while assigned(hp) and (hp.nodetype<>loadn) do begin case hp.nodetype of vecn : begin case tvecnode(hp).left.resulttype.def.deftype of stringdef : begin { this seems OK for shortstring and ansistrings PM } { it is wrong for widestrings !! } len:=1; base:=0; end; arraydef : begin len:=tarraydef(tvecnode(hp).left.resulttype.def).elesize; base:=tarraydef(tvecnode(hp).left.resulttype.def).lowrange; end else Message(parser_e_illegal_expression); end; if is_constintnode(tvecnode(hp).right) then inc(offset,len*(get_ordinal_value(tvecnode(hp).right)-base)) else Message(parser_e_illegal_expression); end; subscriptn : inc(offset,tsubscriptnode(hp).vs.fieldoffset); typeconvn : begin if not(ttypeconvnode(hp).convtype in [tc_equal,tc_proc_2_procvar]) then Message(parser_e_illegal_expression); end; addrn : ; else Message(parser_e_illegal_expression); end; hp:=tunarynode(hp).left; end; srsym:=tloadnode(hp).symtableentry; case srsym.typ of procsym : begin if Tprocsym(srsym).procdef_count>1 then Message(parser_e_no_overloaded_procvars); if po_abstractmethod in tprocsym(srsym).first_procdef.procoptions then Message(type_e_cant_take_address_of_abstract_method) else asmlist[cural].concat(Tai_const.Createname(tprocsym(srsym).first_procdef.mangledname,AT_FUNCTION,offset)); end; globalvarsym : asmlist[cural].concat(Tai_const.Createname(tglobalvarsym(srsym).mangledname,AT_DATA,offset)); typedconstsym : asmlist[cural].concat(Tai_const.Createname(ttypedconstsym(srsym).mangledname,AT_DATA,offset)); labelsym : asmlist[cural].concat(Tai_const.Createname(tlabelsym(srsym).mangledname,AT_LABEL,offset)); constsym : if tconstsym(srsym).consttyp=constresourcestring then asmlist[cural].concat(Tai_const.Createname(make_mangledname('RESOURCESTRINGLIST',tconstsym(srsym).owner,''),AT_DATA,tconstsym(srsym).resstrindex*(4+sizeof(aint)*3)+4+sizeof(aint))) else Message(type_e_variable_id_expected); else Message(type_e_variable_id_expected); end; end else Message(parser_e_illegal_expression); end else { allow typeof(Object type)} if (p.nodetype=inlinen) and (tinlinenode(p).inlinenumber=in_typeof_x) then begin if (tinlinenode(p).left.nodetype=typen) then begin asmlist[cural].concat(Tai_const.createname( tobjectdef(tinlinenode(p).left.resulttype.def).vmt_mangledname,AT_DATA,0)); end else Message(parser_e_illegal_expression); end else Message(parser_e_illegal_expression); p.free; end; setdef: begin p:=comp_expr(true); if p.nodetype=setconstn then begin { be sure to convert to the correct result, else it can generate smallset data instead of normalset (PFV) } inserttypeconv(p,t); { we only allow const sets } if assigned(tsetconstnode(p).left) then Message(parser_e_illegal_expression) else begin { this writing is endian independant } { untrue - because they are considered } { arrays of 32-bit values CEC } if source_info.endian = target_info.endian then begin for l:=0 to p.resulttype.def.size-1 do asmlist[cural].concat(tai_const.create_8bit(Psetbytes(tsetconstnode(p).value_set)^[l])); end else begin { store as longint values in swaped format } j:=0; for l:=0 to ((p.resulttype.def.size-1) div 4) do begin asmlist[cural].concat(tai_const.create_8bit(Psetbytes(tsetconstnode(p).value_set)^[j+3])); asmlist[cural].concat(tai_const.create_8bit(Psetbytes(tsetconstnode(p).value_set)^[j+2])); asmlist[cural].concat(tai_const.create_8bit(Psetbytes(tsetconstnode(p).value_set)^[j+1])); asmlist[cural].concat(tai_const.create_8bit(Psetbytes(tsetconstnode(p).value_set)^[j])); Inc(j,4); end; end; end; end else Message(parser_e_illegal_expression); p.free; end; enumdef: begin p:=comp_expr(true); if p.nodetype=ordconstn then begin if equal_defs(p.resulttype.def,t.def) or is_subequal(p.resulttype.def,t.def) then begin case longint(p.resulttype.def.size) of 1 : asmlist[cural].concat(Tai_const.Create_8bit(Byte(tordconstnode(p).value))); 2 : asmlist[cural].concat(Tai_const.Create_16bit(Word(tordconstnode(p).value))); 4 : asmlist[cural].concat(Tai_const.Create_32bit(Longint(tordconstnode(p).value))); end; end else IncompatibleTypes(p.resulttype.def,t.def); end else Message(parser_e_illegal_expression); p.free; end; stringdef: begin p:=comp_expr(true); { load strval and strlength of the constant tree } if (p.nodetype=stringconstn) or is_widestring(t.def) then begin { convert to the expected string type so that for widestrings strval is a pcompilerwidestring } inserttypeconv(p,t); strlength:=tstringconstnode(p).len; strval:=tstringconstnode(p).value_str; end else if is_constcharnode(p) then begin { strval:=pchar(@tordconstnode(p).value); THIS FAIL on BIG_ENDIAN MACHINES PM } c:=chr(tordconstnode(p).value and $ff); strval:=@c; strlength:=1 end else if is_constresourcestringnode(p) then begin strval:=pchar(tconstsym(tloadnode(p).symtableentry).value.valueptr); strlength:=tconstsym(tloadnode(p).symtableentry).value.len; end else begin Message(parser_e_illegal_expression); strlength:=-1; end; if strlength>=0 then begin case tstringdef(t.def).string_typ of st_shortstring: begin if strlength>=t.def.size then begin message2(parser_w_string_too_long,strpas(strval),tostr(t.def.size-1)); strlength:=t.def.size-1; end; asmlist[cural].concat(Tai_const.Create_8bit(strlength)); { this can also handle longer strings } getmem(ca,strlength+1); move(strval^,ca^,strlength); ca[strlength]:=#0; asmlist[cural].concat(Tai_string.Create_pchar(ca,strlength)); { fillup with spaces if size is shorter } if t.def.size>strlength then begin getmem(ca,t.def.size-strlength); { def.size contains also the leading length, so we } { we have to subtract one } fillchar(ca[0],t.def.size-strlength-1,' '); ca[t.def.size-strlength-1]:=#0; { this can also handle longer strings } asmlist[cural].concat(Tai_string.Create_pchar(ca,t.def.size-strlength-1)); end; end; st_ansistring: begin { an empty ansi string is nil! } if (strlength=0) then asmlist[cural].concat(Tai_const.Create_sym(nil)) else begin objectlibrary.getdatalabel(ll); asmlist[cural].concat(Tai_const.Create_sym(ll)); asmlist[al_const].concat(tai_align.create(const_align(sizeof(aint)))); asmlist[al_const].concat(Tai_const.Create_aint(-1)); asmlist[al_const].concat(Tai_const.Create_aint(strlength)); asmlist[al_const].concat(Tai_label.Create(ll)); getmem(ca,strlength+1); move(strval^,ca^,strlength); { The terminating #0 to be stored in the .data section (JM) } ca[strlength]:=#0; asmlist[al_const].concat(Tai_string.Create_pchar(ca,strlength)); end; end; st_widestring: begin { an empty ansi string is nil! } if (strlength=0) then asmlist[cural].concat(Tai_const.Create_sym(nil)) else begin objectlibrary.getdatalabel(ll); asmlist[cural].concat(Tai_const.Create_sym(ll)); asmlist[al_const].concat(tai_align.create(const_align(sizeof(aint)))); asmlist[al_const].concat(Tai_const.Create_aint(-1)); asmlist[al_const].concat(Tai_const.Create_aint(strlength)); asmlist[al_const].concat(Tai_label.Create(ll)); for i:=0 to strlength-1 do asmlist[al_const].concat(Tai_const.Create_16bit(pcompilerwidestring(strval)^.data[i])); { ending #0 } asmlist[al_const].concat(Tai_const.Create_16bit(0)) end; end; st_longstring: begin internalerror(200107081); end; end; end; p.free; end; arraydef: begin { dynamic array nil } if is_dynamic_array(t.def) then begin { Only allow nil initialization } consume(_NIL); asmlist[cural].concat(Tai_const.Create_sym(nil)); end else if try_to_consume(_LKLAMMER) then begin for l:=tarraydef(t.def).lowrange to tarraydef(t.def).highrange-1 do begin readtypedconst(tarraydef(t.def).elementtype,nil,writable); consume(_COMMA); end; readtypedconst(tarraydef(t.def).elementtype,nil,writable); consume(_RKLAMMER); end else { if array of char then we allow also a string } if is_char(tarraydef(t.def).elementtype.def) then begin p:=comp_expr(true); if p.nodetype=stringconstn then begin len:=tstringconstnode(p).len; { For tp7 the maximum lentgh can be 255 } if (m_tp7 in aktmodeswitches) and (len>255) then len:=255; ca:=tstringconstnode(p).value_str; end else if is_constcharnode(p) then begin c:=chr(tordconstnode(p).value and $ff); ca:=@c; len:=1; end else begin Message(parser_e_illegal_expression); len:=0; end; if len>(tarraydef(t.def).highrange-tarraydef(t.def).lowrange+1) then Message(parser_e_string_larger_array); for i:=tarraydef(t.def).lowrange to tarraydef(t.def).highrange do begin if i+1-tarraydef(t.def).lowrange<=len then begin asmlist[cural].concat(Tai_const.Create_8bit(byte(ca^))); inc(ca); end else {Fill the remaining positions with #0.} asmlist[cural].concat(Tai_const.Create_8bit(0)); end; p.free; end else begin { we want the ( } consume(_LKLAMMER); end; end; procvardef: begin { Procvars and pointers are no longer compatible. } { under tp: =nil or =var under fpc: =nil or =@var } if token=_NIL then begin asmlist[cural].concat(Tai_const.Create_sym(nil)); if (po_methodpointer in tprocvardef(t.def).procoptions) then asmlist[cural].concat(Tai_const.Create_sym(nil)); consume(_NIL); goto myexit; end; { you can't assign a value other than NIL to a typed constant } { which is a "procedure of object", because this also requires } { address of an object/class instance, which is not known at } { compile time (JM) } if (po_methodpointer in tprocvardef(t.def).procoptions) then Message(parser_e_no_procvarobj_const); { parse the rest too, so we can continue with error checking } getprocvardef:=tprocvardef(t.def); p:=comp_expr(true); getprocvardef:=nil; if codegenerror then begin p.free; goto myexit; end; { let type conversion check everything needed } inserttypeconv(p,t); if codegenerror then begin p.free; goto myexit; end; { remove typeconvs, that will normally insert a lea instruction which is not necessary for us } while p.nodetype=typeconvn do begin hp:=ttypeconvnode(p).left; ttypeconvnode(p).left:=nil; p.free; p:=hp; end; { remove addrn which we also don't need here } if p.nodetype=addrn then begin hp:=taddrnode(p).left; taddrnode(p).left:=nil; p.free; p:=hp; end; { we now need to have a loadn with a procsym } if (p.nodetype=loadn) and (tloadnode(p).symtableentry.typ=procsym) then begin asmlist[cural].concat(Tai_const.createname( tprocsym(tloadnode(p).symtableentry).first_procdef.mangledname,AT_FUNCTION,0)); end else Message(parser_e_illegal_expression); p.free; end; { reads a typed constant record } recorddef: begin { KAZ } if (trecorddef(t.def)=rec_tguid) and ((token=_CSTRING) or (token=_CCHAR) or (token=_ID)) then begin p:=comp_expr(true); inserttypeconv(p,cshortstringtype); if p.nodetype=stringconstn then begin s:=strpas(tstringconstnode(p).value_str); p.free; if string2guid(s,tmpguid) then begin asmlist[cural].concat(Tai_const.Create_32bit(longint(tmpguid.D1))); asmlist[cural].concat(Tai_const.Create_16bit(tmpguid.D2)); asmlist[cural].concat(Tai_const.Create_16bit(tmpguid.D3)); for i:=Low(tmpguid.D4) to High(tmpguid.D4) do asmlist[cural].concat(Tai_const.Create_8bit(tmpguid.D4[i])); end else Message(parser_e_improper_guid_syntax); end else begin p.free; Message(parser_e_illegal_expression); goto myexit; end; end else begin consume(_LKLAMMER); sorg:=''; aktpos:=0; srsym := tsym(trecorddef(t.def).symtable.symindex.first); recsym := nil; while token<>_RKLAMMER do begin s:=pattern; sorg:=orgpattern; consume(_ID); consume(_COLON); error := false; recsym := tsym(trecorddef(t.def).symtable.search(s)); if not assigned(recsym) then begin Message1(sym_e_illegal_field,sorg); error := true; end; if (not error) and (not assigned(srsym) or (s <> srsym.name)) then { possible variant record (JM) } begin { All parts of a variant start at the same offset } { Also allow jumping from one variant part to another, } { as long as the offsets match } if (assigned(srsym) and (tfieldvarsym(recsym).fieldoffset = tfieldvarsym(srsym).fieldoffset)) or { srsym is not assigned after parsing w2 in the } { typed const in the next example: } { type tr = record case byte of } { 1: (l1,l2: dword); } { 2: (w1,w2: word); } { end; } { const r: tr = (w1:1;w2:1;l2:5); } (tfieldvarsym(recsym).fieldoffset = aktpos) then srsym := recsym { going backwards isn't allowed in any mode } else if (tfieldvarsym(recsym).fieldoffsetaktpos then for i:=1 to tfieldvarsym(srsym).fieldoffset-aktpos do asmlist[cural].concat(Tai_const.Create_8bit(0)); { new position } aktpos:=tfieldvarsym(srsym).fieldoffset+tfieldvarsym(srsym).vartype.def.size; { read the data } readtypedconst(tfieldvarsym(srsym).vartype,nil,writable); { keep previous field for checking whether whole } { record was initialized (JM) } recsym := srsym; { goto next field } srsym := tsym(srsym.indexnext); if token=_SEMICOLON then consume(_SEMICOLON) else break; end; end; { are there any fields left? } if assigned(srsym) and { don't complain if there only come other variant parts } { after the last initialized field } ((recsym=nil) or (tfieldvarsym(srsym).fieldoffset > tfieldvarsym(recsym).fieldoffset)) then Message1(parser_w_skipped_fields_after,sorg); for i:=1 to t.def.size-aktpos do asmlist[cural].concat(Tai_const.Create_8bit(0)); consume(_RKLAMMER); end; end; { reads a typed object } objectdef: begin if is_class_or_interface(t.def) then begin p:=comp_expr(true); if p.nodetype<>niln then begin Message(parser_e_type_const_not_possible); consume_all_until(_RKLAMMER); end else begin asmlist[cural].concat(Tai_const.Create_sym(nil)); end; p.free; end { for objects we allow it only if it doesn't contain a vmt } else if (oo_has_vmt in tobjectdef(t.def).objectoptions) and (m_fpc in aktmodeswitches) then Message(parser_e_type_const_not_possible) else begin consume(_LKLAMMER); aktpos:=0; while token<>_RKLAMMER do begin s:=pattern; sorg:=orgpattern; consume(_ID); consume(_COLON); srsym:=nil; obj:=tobjectdef(t.def); symt:=obj.symtable; while (srsym=nil) and assigned(symt) do begin srsym:=tsym(symt.search(s)); if assigned(obj) then obj:=obj.childof; if assigned(obj) then symt:=obj.symtable else symt:=nil; end; if srsym=nil then begin Message1(sym_e_id_not_found,sorg); consume_all_until(_SEMICOLON); end else with tfieldvarsym(srsym) do begin { check position } if fieldoffsetaktpos then for i:=1 to fieldoffset-aktpos do asmlist[cural].concat(Tai_const.Create_8bit(0)); { new position } aktpos:=fieldoffset+vartype.def.size; { read the data } readtypedconst(vartype,nil,writable); if token=_SEMICOLON then consume(_SEMICOLON) else break; end; end; if not(m_fpc in aktmodeswitches) and (oo_has_vmt in tobjectdef(t.def).objectoptions) and (tobjectdef(t.def).vmt_offset>=aktpos) then begin for i:=1 to tobjectdef(t.def).vmt_offset-aktpos do asmlist[cural].concat(tai_const.create_8bit(0)); asmlist[cural].concat(tai_const.createname(tobjectdef(t.def).vmt_mangledname,AT_DATA,0)); { this is more general } aktpos:=tobjectdef(t.def).vmt_offset + sizeof(aint); end; for i:=1 to t.def.size-aktpos do asmlist[cural].concat(Tai_const.Create_8bit(0)); consume(_RKLAMMER); end; end; errordef: begin { try to consume something useful } if token=_LKLAMMER then consume_all_until(_RKLAMMER) else consume_all_until(_SEMICOLON); end; else Message(parser_e_type_const_not_possible); end; myexit: block_type:=old_block_type; end; {$ifdef fpc} {$maxfpuregisters default} {$endif fpc} end.