cgobj.pas 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. {
  2. $Id$
  3. Copyright (c) 1998-2000 by Florian Klaempfl
  4. Member of the Free Pascal development team
  5. This unit implements the basic code generator object
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. unit cgobj;
  20. interface
  21. uses
  22. cobjects,aasm,symtable,symconst,cpuasm,cpubase,cgbase,cpuinfo,tainst;
  23. type
  24. qword = comp;
  25. talignment = (AM_NATURAL,AM_NONE,AM_2BYTE,AM_4BYTE,AM_8BYTE);
  26. pcg = ^tcg;
  27. tcg = object
  28. scratch_register_array_pointer : aword;
  29. unusedscratchregisters : tregisterset;
  30. alignment : talignment;
  31. {************************************************}
  32. { basic routines }
  33. constructor init;
  34. destructor done;virtual;
  35. procedure a_label(list : paasmoutput;l : pasmlabel);virtual;
  36. { allocates register r by inserting a pai_realloc record }
  37. procedure a_reg_alloc(list : paasmoutput;r : tregister);
  38. { deallocates register r by inserting a pa_regdealloc record}
  39. procedure a_reg_dealloc(list : paasmoutput;r : tregister);
  40. { returns a register for use as scratch register }
  41. function get_scratch_reg(list : paasmoutput) : tregister;
  42. { releases a scratch register }
  43. procedure free_scratch_reg(list : paasmoutput;r : tregister);
  44. {************************************************}
  45. { code generation for subroutine entry/exit code }
  46. { initilizes data of type t }
  47. { if is_already_ref is true then the routines assumes }
  48. { that r points to the data to initialize }
  49. procedure g_initialize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  50. { finalizes data of type t }
  51. { if is_already_ref is true then the routines assumes }
  52. { that r points to the data to finalizes }
  53. procedure g_finalize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  54. { helper routines }
  55. procedure g_initialize_data(list : paasmoutput;p : psym);
  56. procedure g_incr_data(list : paasmoutput;p : psym);
  57. procedure g_finalize_data(list : paasmoutput;p : pnamedindexobject);
  58. procedure g_copyvalueparas(list : paasmoutput;p : pnamedindexobject);
  59. procedure g_finalizetempansistrings(list : paasmoutput);
  60. procedure g_entrycode(list : paasmoutput;
  61. const proc_names : tstringcontainer;make_global : boolean;
  62. stackframe : longint;var parasize : longint;
  63. var nostackframe : boolean;inlined : boolean);
  64. procedure g_exitcode(list : paasmoutput;parasize : longint;
  65. nostackframe,inlined : boolean);
  66. { string helper routines }
  67. procedure g_decrstrref(list : paasmoutput;const ref : treference;t : pdef);
  68. procedure g_removetemps(list : paasmoutput;p : plinkedlist);
  69. { passing parameters, per default the parameter is pushed }
  70. { nr gives the number of the parameter (enumerated from }
  71. { left to right), this allows to move the parameter to }
  72. { register, if the cpu supports register calling }
  73. { conventions }
  74. procedure a_param_reg(list : paasmoutput;size : tcgsize;r : tregister;nr : longint);virtual;
  75. procedure a_param_const(list : paasmoutput;size : tcgsize;a : aword;nr : longint);virtual;
  76. procedure a_param_ref(list : paasmoutput;size : tcgsize;const r : treference;nr : longint);virtual;
  77. procedure a_paramaddr_ref(list : paasmoutput;const r : treference;nr : longint);virtual;
  78. {**********************************}
  79. { these methods must be overriden: }
  80. { Remarks:
  81. * If a method specifies a size you have only to take care
  82. of that number of bits, i.e. load_const_reg with OP_8 must
  83. only load the lower 8 bit of the specified register
  84. the rest of the register can be undefined
  85. if necessary the compiler will call a method
  86. to zero or sign extend the register
  87. * The a_load_XX_XX with OP_64 needn't to be
  88. implemented for 32 bit
  89. processors, the code generator takes care of that
  90. * the addr size is for work with the natural pointer
  91. size
  92. * the procedures without fpu/mm are only for integer usage
  93. * normally the first location is the source and the
  94. second the destination
  95. }
  96. procedure a_call_name(list : paasmoutput;const s : string;
  97. offset : longint);virtual;
  98. { move instructions }
  99. procedure a_load_const_reg(list : paasmoutput;size : tcgsize;a : aword;register : tregister);virtual;
  100. procedure a_load_reg_ref(list : paasmoutput;size : tcgsize;register : tregister;const ref : treference);virtual;
  101. procedure a_load_ref_reg(list : paasmoutput;size : tcgsize;const ref : treference;register : tregister);virtual;
  102. procedure a_load_reg_reg(list : paasmoutput;size : tcgsize;reg1,reg2 : tregister);virtual;
  103. { comparison operations }
  104. procedure a_cmp_reg_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  105. l : pasmlabel);virtual;
  106. procedure a_cmp_reg_reg_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : pasmlabel);
  107. procedure a_cmp_reg_ref_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg : tregister;l : pasmlabel);
  108. procedure a_cmp_ref_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  109. l : pasmlabel);
  110. procedure a_jmp_cond(list : paasmoutput;cond : TOpCmp;l: pasmlabel);
  111. procedure a_loadaddress_ref_reg(list : paasmoutput;const ref : treference;r : tregister);virtual;
  112. procedure g_stackframe_entry(list : paasmoutput;localsize : longint);virtual;
  113. { restores the frame pointer at procedure exit, for the }
  114. { i386 it generates a simple leave }
  115. procedure g_restore_frame_pointer(list : paasmoutput);virtual;
  116. { some processors like the PPC doesn't allow to change the stack in }
  117. { a procedure, so we need to maintain an extra stack for the }
  118. { result values of setjmp in exception code }
  119. { this two procedures are for pushing an exception value, }
  120. { they can use the scratch registers }
  121. procedure g_push_exception_value_reg(list : paasmoutput;reg : tregister);virtual;
  122. procedure g_push_exception_value_const(list : paasmoutput;reg : tregister);virtual;
  123. { that procedure pops a exception value }
  124. procedure g_pop_exception_value_reg(list : paasmoutput;reg : tregister);virtual;
  125. procedure g_return_from_proc(list : paasmoutput;parasize : aword);virtual;
  126. {********************************************************}
  127. { these methods can be overriden for extra functionality }
  128. { the following methods do nothing: }
  129. procedure g_interrupt_stackframe_entry(list : paasmoutput);virtual;
  130. procedure g_interrupt_stackframe_exit(list : paasmoutput);virtual;
  131. procedure g_profilecode(list : paasmoutput);virtual;
  132. procedure g_stackcheck(list : paasmoutput;stackframesize : longint);virtual;
  133. procedure a_load_const_ref(list : paasmoutput;size : tcgsize;a : aword;const ref : treference);virtual;
  134. procedure g_maybe_loadself(list : paasmoutput);virtual;
  135. { copies len bytes from the source to destination, if }
  136. { loadref is true, it assumes that it first must load }
  137. { the source address from the memory location where }
  138. { source points to }
  139. procedure g_concatcopy(list : paasmoutput;const source,dest : treference;len : aword;loadref : boolean);virtual;
  140. { uses the addr of ref as param, was emitpushreferenceaddr }
  141. procedure a_param_ref_addr(list : paasmoutput;r : treference;nr : longint);virtual;
  142. end;
  143. var
  144. cg : pcg; { this is the main code generator class }
  145. implementation
  146. uses
  147. strings,globals,globtype,options,files,gdb,systems,
  148. ppu,verbose,types,tgobj,tgcpu;
  149. {*****************************************************************************
  150. basic functionallity
  151. ******************************************************************************}
  152. constructor tcg.init;
  153. var
  154. i : longint;
  155. begin
  156. scratch_register_array_pointer:=1;
  157. for i:=1 to max_scratch_regs do
  158. include(unusedscratchregisters,scratch_regs[i]);
  159. end;
  160. destructor tcg.done;
  161. begin
  162. end;
  163. procedure tcg.a_reg_alloc(list : paasmoutput;r : tregister);
  164. begin
  165. list^.concat(new(pairegalloc,alloc(r)));
  166. end;
  167. procedure tcg.a_reg_dealloc(list : paasmoutput;r : tregister);
  168. begin
  169. list^.concat(new(pairegalloc,dealloc(r)));
  170. end;
  171. procedure tcg.a_label(list : paasmoutput;l : pasmlabel);
  172. begin
  173. list^.concat(new(pai_label,init(l)));
  174. end;
  175. function tcg.get_scratch_reg(list : paasmoutput) : tregister;
  176. var
  177. r : tregister;
  178. i : longint;
  179. begin
  180. if unusedscratchregisters=[] then
  181. internalerror(68996);
  182. for i:=scratch_register_array_pointer to
  183. (scratch_register_array_pointer+max_scratch_regs) do
  184. if scratch_regs[(i mod max_scratch_regs)+1] in unusedscratchregisters then
  185. begin
  186. r:=scratch_regs[(i mod max_scratch_regs)+1];
  187. break;
  188. end;
  189. exclude(unusedscratchregisters,r);
  190. inc(scratch_register_array_pointer);
  191. if scratch_register_array_pointer>max_scratch_regs then
  192. scratch_register_array_pointer:=1;
  193. a_reg_alloc(list,r);
  194. get_scratch_reg:=r;
  195. end;
  196. procedure tcg.free_scratch_reg(list : paasmoutput;r : tregister);
  197. begin
  198. include(unusedscratchregisters,r);
  199. a_reg_dealloc(list,r);
  200. end;
  201. {*****************************************************************************
  202. this methods must be overridden for extra functionality
  203. ******************************************************************************}
  204. procedure tcg.g_interrupt_stackframe_entry(list : paasmoutput);
  205. begin
  206. end;
  207. procedure tcg.g_interrupt_stackframe_exit(list : paasmoutput);
  208. begin
  209. end;
  210. procedure tcg.g_profilecode(list : paasmoutput);
  211. begin
  212. end;
  213. {*****************************************************************************
  214. for better code generation these methods should be overridden
  215. ******************************************************************************}
  216. procedure tcg.a_param_const(list : paasmoutput;size : tcgsize;a : aword;nr : longint);
  217. var
  218. hr : tregister;
  219. begin
  220. hr:=get_scratch_reg(list);
  221. a_load_const_reg(list,size,a,hr);
  222. a_param_reg(list,size,hr,nr);
  223. free_scratch_reg(list,hr);
  224. end;
  225. procedure tcg.a_param_ref(list : paasmoutput;size : tcgsize;const r : treference;nr : longint);
  226. var
  227. hr : tregister;
  228. begin
  229. hr:=get_scratch_reg(list);
  230. a_load_ref_reg(list,size,r,hr);
  231. a_param_reg(list,size,hr,nr);
  232. free_scratch_reg(list,hr);
  233. end;
  234. procedure tcg.a_param_ref_addr(list : paasmoutput;r : treference;nr : longint);
  235. var
  236. hr : tregister;
  237. begin
  238. hr:=get_scratch_reg(list);
  239. a_loadaddress_ref_reg(list,r,hr);
  240. a_param_reg(list,OS_ADDR,hr,nr);
  241. free_scratch_reg(list,hr);
  242. end;
  243. procedure tcg.g_stackcheck(list : paasmoutput;stackframesize : longint);
  244. begin
  245. a_param_const(list,OS_32,stackframesize,1);
  246. a_call_name(list,'FPC_STACKCHECK',0);
  247. end;
  248. procedure tcg.a_load_const_ref(list : paasmoutput;size : tcgsize;a : aword;const ref : treference);
  249. var
  250. hr : tregister;
  251. begin
  252. hr:=get_scratch_reg(list);
  253. a_load_const_reg(list,size,a,hr);
  254. a_load_reg_ref(list,size,hr,ref);
  255. free_scratch_reg(list,hr);
  256. end;
  257. procedure tcg.g_concatcopy(list : paasmoutput;const source,dest : treference;len : aword;loadref : boolean);
  258. begin
  259. abstract;
  260. end;
  261. {*****************************************************************************
  262. String helper routines
  263. *****************************************************************************}
  264. procedure tcg.g_removetemps(list : paasmoutput;p : plinkedlist);
  265. var
  266. hp : ptemptodestroy;
  267. pushedregs : tpushed;
  268. begin
  269. hp:=ptemptodestroy(p^.first);
  270. if not(assigned(hp)) then
  271. exit;
  272. tg.pushusedregisters(pushedregs,$ff);
  273. while assigned(hp) do
  274. begin
  275. if is_ansistring(hp^.typ) then
  276. begin
  277. g_decrstrref(list,hp^.address,hp^.typ);
  278. tg.ungetiftemp(hp^.address);
  279. end;
  280. hp:=ptemptodestroy(hp^.next);
  281. end;
  282. tg.popusedregisters(pushedregs);
  283. end;
  284. procedure tcg.g_decrstrref(list : paasmoutput;const ref : treference;t : pdef);
  285. var
  286. pushedregs : tpushed;
  287. begin
  288. tg.pushusedregisters(pushedregs,$ff);
  289. a_param_ref_addr(list,ref,1);
  290. if is_ansistring(t) then
  291. a_call_name(list,'FPC_ANSISTR_DECR_REF',0)
  292. else if is_widestring(t) then
  293. a_call_name(list,'FPC_WIDESTR_DECR_REF',0)
  294. else internalerror(58993);
  295. tg.popusedregisters(pushedregs);
  296. end;
  297. {*****************************************************************************
  298. Code generation for subroutine entry- and exit code
  299. *****************************************************************************}
  300. { initilizes data of type t }
  301. { if is_already_ref is true then the routines assumes }
  302. { that r points to the data to initialize }
  303. procedure tcg.g_initialize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  304. var
  305. hr : treference;
  306. begin
  307. if is_ansistring(t) or
  308. is_widestring(t) then
  309. a_load_const_ref(list,OS_8,0,ref)
  310. else
  311. begin
  312. reset_reference(hr);
  313. hr.symbol:=t^.get_inittable_label;
  314. a_param_ref_addr(list,hr,2);
  315. if is_already_ref then
  316. a_param_ref(list,OS_ADDR,ref,1)
  317. else
  318. a_param_ref_addr(list,ref,1);
  319. a_call_name(list,'FPC_INITIALIZE',0);
  320. end;
  321. end;
  322. procedure tcg.g_finalize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  323. var
  324. r : treference;
  325. begin
  326. if is_ansistring(t) or
  327. is_widestring(t) then
  328. begin
  329. g_decrstrref(list,ref,t);
  330. end
  331. else
  332. begin
  333. reset_reference(r);
  334. r.symbol:=t^.get_inittable_label;
  335. a_param_ref_addr(list,r,2);
  336. if is_already_ref then
  337. a_paramaddr_ref(list,ref,1)
  338. else
  339. a_param_ref_addr(list,ref,1);
  340. a_call_name(list,'FPC_FINALIZE',0);
  341. end;
  342. end;
  343. { generates the code for initialisation of local data }
  344. procedure tcg.g_initialize_data(list : paasmoutput;p : psym);
  345. var
  346. hr : treference;
  347. begin
  348. if (psym(p)^.typ=varsym) and
  349. assigned(pvarsym(p)^.vartype.def) and
  350. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  351. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  352. pvarsym(p)^.vartype.def^.needs_inittable then
  353. begin
  354. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  355. reset_reference(hr);
  356. if psym(p)^.owner^.symtabletype=localsymtable then
  357. begin
  358. hr.base:=procinfo^.framepointer;
  359. hr.offset:=-pvarsym(p)^.address;
  360. end
  361. else
  362. begin
  363. hr.symbol:=newasmsymbol(pvarsym(p)^.mangledname);
  364. end;
  365. g_initialize(list,pvarsym(p)^.vartype.def,hr,false);
  366. end;
  367. end;
  368. { generates the code for incrementing the reference count of parameters }
  369. procedure tcg.g_incr_data(list : paasmoutput;p : psym);
  370. var
  371. hr : treference;
  372. begin
  373. if (psym(p)^.typ=varsym) and
  374. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  375. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  376. pvarsym(p)^.vartype.def^.needs_inittable and
  377. ((pvarsym(p)^.varspez=vs_value)) then
  378. begin
  379. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  380. reset_reference(hr);
  381. hr.symbol:=pvarsym(p)^.vartype.def^.get_inittable_label;
  382. a_param_ref_addr(list,hr,2);
  383. reset_reference(hr);
  384. hr.base:=procinfo^.framepointer;
  385. hr.offset:=pvarsym(p)^.address+procinfo^.call_offset;
  386. a_param_ref_addr(list,hr,1);
  387. reset_reference(hr);
  388. a_call_name(list,'FPC_ADDREF',0);
  389. end;
  390. end;
  391. { generates the code for finalisation of local data }
  392. procedure tcg.g_finalize_data(list : paasmoutput;p : pnamedindexobject);
  393. var
  394. hr : treference;
  395. begin
  396. if (psym(p)^.typ=varsym) and
  397. assigned(pvarsym(p)^.vartype.def) and
  398. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  399. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  400. pvarsym(p)^.vartype.def^.needs_inittable then
  401. begin
  402. { not all kind of parameters need to be finalized }
  403. if (psym(p)^.owner^.symtabletype=parasymtable) and
  404. ((pvarsym(p)^.varspez=vs_var) or
  405. (pvarsym(p)^.varspez=vs_const) { and
  406. (dont_copy_const_param(pvarsym(p)^.definition)) } ) then
  407. exit;
  408. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  409. reset_reference(hr);
  410. case psym(p)^.owner^.symtabletype of
  411. localsymtable:
  412. begin
  413. hr.base:=procinfo^.framepointer;
  414. hr.offset:=-pvarsym(p)^.address;
  415. end;
  416. parasymtable:
  417. begin
  418. hr.base:=procinfo^.framepointer;
  419. hr.offset:=pvarsym(p)^.address+procinfo^.call_offset;
  420. end;
  421. else
  422. hr.symbol:=newasmsymbol(pvarsym(p)^.mangledname);
  423. end;
  424. g_finalize(list,pvarsym(p)^.vartype.def,hr,false);
  425. end;
  426. end;
  427. { generates the code to make local copies of the value parameters }
  428. procedure tcg.g_copyvalueparas(list : paasmoutput;p : pnamedindexobject);
  429. begin
  430. runerror(255);
  431. end;
  432. var
  433. _list : paasmoutput;
  434. { wrappers for the methods, because TP doesn't know procedures }
  435. { of objects }
  436. procedure _copyvalueparas(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  437. begin
  438. cg^.g_copyvalueparas(_list,s);
  439. end;
  440. procedure tcg.g_finalizetempansistrings(list : paasmoutput);
  441. var
  442. hp : ptemprecord;
  443. hr : treference;
  444. begin
  445. hp:=tg.templist;
  446. while assigned(hp) do
  447. begin
  448. if hp^.temptype in [tt_ansistring,tt_freeansistring] then
  449. begin
  450. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  451. reset_reference(hr);
  452. hr.base:=procinfo^.framepointer;
  453. hr.offset:=hp^.pos;
  454. a_param_ref_addr(list,hr,1);
  455. a_call_name(list,'FPC_ANSISTR_DECR_REF',0);
  456. end;
  457. hp:=hp^.next;
  458. end;
  459. end;
  460. procedure _finalize_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  461. begin
  462. cg^.g_finalize_data(_list,s);
  463. end;
  464. procedure _incr_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  465. begin
  466. cg^.g_incr_data(_list,psym(s));
  467. end;
  468. procedure _initialize_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  469. begin
  470. cg^.g_initialize_data(_list,psym(s));
  471. end;
  472. { generates the entry code for a procedure }
  473. procedure tcg.g_entrycode(list : paasmoutput;const proc_names:Tstringcontainer;make_global:boolean;
  474. stackframe:longint;var parasize:longint;var nostackframe:boolean;
  475. inlined : boolean);
  476. var
  477. hs : string;
  478. hp : pused_unit;
  479. initcode : taasmoutput;
  480. {$ifdef GDB}
  481. stab_function_name : Pai_stab_function_name;
  482. {$endif GDB}
  483. hr : treference;
  484. r : tregister;
  485. begin
  486. { Align }
  487. if (not inlined) then
  488. begin
  489. { gprof uses 16 byte granularity !! }
  490. if (cs_profile in aktmoduleswitches) then
  491. list^.insert(new(pai_align,init(16)))
  492. else
  493. if not(cs_littlesize in aktglobalswitches) then
  494. list^.insert(new(pai_align,init(4)));
  495. end;
  496. { save registers on cdecl }
  497. if (po_savestdregs in aktprocsym^.definition^.procoptions) then
  498. begin
  499. for r:=firstreg to lastreg do
  500. begin
  501. if (r in registers_saved_on_cdecl) then
  502. if (r in (tg.availabletempregsint+
  503. tg.availabletempregsfpu+
  504. tg.availabletempregsmm)) then
  505. begin
  506. if not(r in tg.usedinproc) then
  507. {!!!!!!!!!!!! a_push_reg(list,r) }
  508. end
  509. else
  510. {!!!!!!!! a_push_reg(list,r) };
  511. end;
  512. end;
  513. { omit stack frame ? }
  514. if not inlined then
  515. if procinfo^.framepointer=stack_pointer then
  516. begin
  517. CGMessage(cg_d_stackframe_omited);
  518. nostackframe:=true;
  519. if (aktprocsym^.definition^.proctypeoption in [potype_unitinit,potype_proginit,potype_unitfinalize]) then
  520. parasize:=0
  521. else
  522. parasize:=aktprocsym^.definition^.parast^.datasize+procinfo^.call_offset-pointersize;
  523. end
  524. else
  525. begin
  526. if (aktprocsym^.definition^.proctypeoption in [potype_unitinit,potype_proginit,potype_unitfinalize]) then
  527. parasize:=0
  528. else
  529. parasize:=aktprocsym^.definition^.parast^.datasize+procinfo^.call_offset-pointersize*2;
  530. nostackframe:=false;
  531. if (po_interrupt in aktprocsym^.definition^.procoptions) then
  532. g_interrupt_stackframe_entry(list);
  533. g_stackframe_entry(list,stackframe);
  534. if (cs_check_stack in aktlocalswitches) and
  535. (tf_supports_stack_checking in target_info.flags) then
  536. g_stackcheck(@initcode,stackframe);
  537. end;
  538. if cs_profile in aktmoduleswitches then
  539. g_profilecode(@initcode);
  540. if (not inlined) and (aktprocsym^.definition^.proctypeoption in [potype_unitinit]) then
  541. begin
  542. { needs the target a console flags ? }
  543. if tf_needs_isconsole in target_info.flags then
  544. begin
  545. hr.symbol:=newasmsymbol('U_'+target_info.system_unit+'_ISCONSOLE');
  546. if apptype=at_cui then
  547. a_load_const_ref(list,OS_8,1,hr)
  548. else
  549. a_load_const_ref(list,OS_8,0,hr);
  550. dispose(hr.symbol,done);
  551. end;
  552. hp:=pused_unit(usedunits.first);
  553. while assigned(hp) do
  554. begin
  555. { call the unit init code and make it external }
  556. if (hp^.u^.flags and uf_init)<>0 then
  557. a_call_name(list,
  558. 'INIT$$'+hp^.u^.modulename^,0);
  559. hp:=Pused_unit(hp^.next);
  560. end;
  561. end;
  562. {$ifdef dummy}
  563. { a constructor needs a help procedure }
  564. if (aktprocsym^.definition^.options and poconstructor)<>0 then
  565. begin
  566. if procinfo^._class^.isclass then
  567. begin
  568. list^.concat(new(paicpu,op_sym(A_CALL,S_NO,newasmsymbol('FPC_NEW_CLASS'))));
  569. list^.concat(new(paicpu,op_cond_sym(A_Jcc,C_Z,S_NO,quickexitlabel)));
  570. end
  571. else
  572. begin
  573. {
  574. list^.insert(new(pai_labeled,init(A_JZ,quickexitlabel)));
  575. list^.insert(new(paicpu,op_csymbol(A_CALL,S_NO,
  576. newcsymbol('FPC_HELP_CONSTRUCTOR',0))));
  577. list^.insert(new(paicpu,op_const_reg(A_MOV,S_L,procinfo^._class^.vmt_offset,R_EDI)));
  578. concat_external('FPC_HELP_CONSTRUCTOR',EXT_NEAR);
  579. }
  580. end;
  581. end;
  582. {$endif dummy}
  583. {$ifdef GDB}
  584. if (cs_debuginfo in aktmoduleswitches) then
  585. list^.insert(new(pai_force_line,init));
  586. {$endif GDB}
  587. { initialize return value }
  588. if assigned(procinfo^.returntype.def) and
  589. is_ansistring(procinfo^.returntype.def) or
  590. is_widestring(procinfo^.returntype.def) then
  591. begin
  592. reset_reference(hr);
  593. hr.offset:=procinfo^.return_offset;
  594. hr.base:=procinfo^.framepointer;
  595. a_load_const_ref(list,OS_32,0,hr);
  596. end;
  597. _list:=list;
  598. { generate copies of call by value parameters }
  599. if (po_assembler in aktprocsym^.definition^.procoptions) then
  600. aktprocsym^.definition^.parast^.foreach({$ifdef FPC}@{$endif FPC}_copyvalueparas);
  601. { initialisizes local data }
  602. aktprocsym^.definition^.localst^.foreach({$ifdef FPC}@{$endif FPC}_initialize_data);
  603. { add a reference to all call by value/const parameters }
  604. aktprocsym^.definition^.parast^.foreach({$ifdef FPC}@{$endif FPC}_incr_data);
  605. if (cs_profile in aktmoduleswitches) or
  606. (aktprocsym^.definition^.owner^.symtabletype=globalsymtable) or
  607. (assigned(procinfo^._class) and (procinfo^._class^.owner^.symtabletype=globalsymtable)) then
  608. make_global:=true;
  609. if not inlined then
  610. begin
  611. hs:=proc_names.get;
  612. {$ifdef GDB}
  613. if (cs_debuginfo in aktmoduleswitches) and target_os.use_function_relative_addresses then
  614. stab_function_name := new(pai_stab_function_name,init(strpnew(hs)));
  615. {$endif GDB}
  616. { insert the names for the procedure }
  617. while hs<>'' do
  618. begin
  619. if make_global then
  620. exprasmlist^.insert(new(pai_symbol,initname_global(hs,0)))
  621. else
  622. exprasmlist^.insert(new(pai_symbol,initname(hs,0)));
  623. {$ifdef GDB}
  624. if (cs_debuginfo in aktmoduleswitches) then
  625. begin
  626. if target_os.use_function_relative_addresses then
  627. list^.insert(new(pai_stab_function_name,init(strpnew(hs))));
  628. end;
  629. {$endif GDB}
  630. hs:=proc_names.get;
  631. end;
  632. end;
  633. {$ifdef GDB}
  634. if (not inlined) and (cs_debuginfo in aktmoduleswitches) then
  635. begin
  636. if target_os.use_function_relative_addresses then
  637. list^.insert(stab_function_name);
  638. if make_global or ((procinfo^.flags and pi_is_global) <> 0) then
  639. aktprocsym^.is_global := True;
  640. list^.insert(new(pai_stabs,init(aktprocsym^.stabstring)));
  641. aktprocsym^.isstabwritten:=true;
  642. end;
  643. {$endif GDB}
  644. end;
  645. procedure tcg.g_exitcode(list : paasmoutput;parasize:longint;nostackframe,inlined:boolean);
  646. var
  647. {$ifdef GDB}
  648. mangled_length : longint;
  649. p : pchar;
  650. {$endif GDB}
  651. nofinal,noreraiselabel : pasmlabel;
  652. hr : treference;
  653. r : tregister;
  654. begin
  655. if aktexitlabel^.is_used then
  656. list^.insert(new(pai_label,init(aktexitlabel)));
  657. { call the destructor help procedure }
  658. if (aktprocsym^.definition^.proctypeoption=potype_destructor) then
  659. begin
  660. if procinfo^._class^.is_class then
  661. a_call_name(list,'FPC_DISPOSE_CLASS',0)
  662. else
  663. begin
  664. if procinfo^._class^.needs_inittable then
  665. begin
  666. getlabel(nofinal);
  667. {!!!!!!!!!!
  668. reset_reference(hr);
  669. hr.base:=R_EBP;
  670. hr.offset:=8;
  671. a_cmp_reg_const_label(list,OS_ADDR,OZ_EQ,
  672. }
  673. reset_reference(hr);
  674. hr.symbol:=procinfo^._class^.get_inittable_label;
  675. a_paramaddr_ref(list,hr,2);
  676. a_param_reg(list,OS_ADDR,self_pointer,1);
  677. a_call_name(list,'FPC_FINALIZE',0);
  678. a_label(list,nofinal);
  679. end;
  680. { vmt_offset_reg can be a scratch register, }
  681. { but it must be always the same }
  682. a_reg_alloc(list,vmt_offset_reg);
  683. a_load_const_reg(list,OS_32,procinfo^._class^.vmt_offset,vmt_offset_reg);
  684. a_call_name(list,'FPC_HELP_DESTRUCTOR',0);
  685. a_reg_dealloc(list,vmt_offset_reg);
  686. end;
  687. end;
  688. { finalize temporary data }
  689. g_finalizetempansistrings(list);
  690. _list:=list;
  691. { finalize local data }
  692. aktprocsym^.definition^.localst^.foreach({$ifndef TP}@{$endif}_finalize_data);
  693. { finalize paras data }
  694. if assigned(aktprocsym^.definition^.parast) then
  695. aktprocsym^.definition^.parast^.foreach({$ifndef TP}@{$endif}_finalize_data);
  696. { do we need to handle exceptions because of ansi/widestrings ? }
  697. if (procinfo^.flags and pi_needs_implicit_finally)<>0 then
  698. begin
  699. getlabel(noreraiselabel);
  700. a_call_name(list,'FPC_POPADDRSTACK',0);
  701. a_reg_alloc(list,accumulator);
  702. g_pop_exception_value_reg(list,accumulator);
  703. a_cmp_reg_const_label(list,OS_32,OC_EQ,0,accumulator,noreraiselabel);
  704. a_reg_dealloc(list,accumulator);
  705. { must be the return value finalized before reraising the exception? }
  706. if (procinfo^.returntype.def<>pdef(voiddef)) and
  707. (procinfo^.returntype.def^.needs_inittable) and
  708. ((procinfo^.returntype.def^.deftype<>objectdef) or
  709. not(pobjectdef(procinfo^.returntype.def)^.is_class)) then
  710. begin
  711. reset_reference(hr);
  712. hr.offset:=procinfo^.return_offset;
  713. hr.base:=procinfo^.framepointer;
  714. g_finalize(list,procinfo^.returntype.def,hr,ret_in_param(procinfo^.returntype.def));
  715. end;
  716. a_call_name(list,'FPC_RERAISE',0);
  717. a_label(list,noreraiselabel);
  718. end;
  719. { call __EXIT for main program }
  720. if (not DLLsource) and (not inlined) and (aktprocsym^.definition^.proctypeoption=potype_proginit) then
  721. a_call_name(list,'FPC_DO_EXIT',0);
  722. { handle return value }
  723. if not(po_assembler in aktprocsym^.definition^.procoptions) then
  724. if (aktprocsym^.definition^.proctypeoption<>potype_constructor) then
  725. { handle_return_value(inlined) }
  726. else
  727. begin
  728. { return self in EAX }
  729. a_label(list,quickexitlabel);
  730. a_reg_alloc(list,accumulator);
  731. a_load_reg_reg(list,OS_ADDR,self_pointer,accumulator);
  732. a_reg_dealloc(list,self_pointer);
  733. a_label(list,quickexitlabel);
  734. { we can't clear the zero flag because the Alpha }
  735. { for example doesn't have flags, we have to compare }
  736. { the accu. in the caller }
  737. end;
  738. { stabs uses the label also ! }
  739. if aktexit2label^.is_used or
  740. ((cs_debuginfo in aktmoduleswitches) and not inlined) then
  741. a_label(list,aktexit2label);
  742. {$ifdef dummy}
  743. { should we restore edi ? }
  744. { for all i386 gcc implementations }
  745. {!!!!!!!!!!! I don't know how to handle register saving yet }
  746. if (po_savestdregs in aktprocsym^.definition^.procoptions) then
  747. begin
  748. if (aktprocsym^.definition^.usedregisters and ($80 shr byte(R_EBX)))<>0 then
  749. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_EBX)));
  750. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_ESI)));
  751. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_EDI)));
  752. { here we could reset R_EBX
  753. but that is risky because it only works
  754. if genexitcode is called after genentrycode
  755. so lets skip this for the moment PM
  756. aktprocsym^.definition^.usedregisters:=
  757. aktprocsym^.definition^.usedregisters or not ($80 shr byte(R_EBX));
  758. }
  759. end;
  760. {$endif dummy}
  761. if not(nostackframe) and not inlined then
  762. g_restore_frame_pointer(list);
  763. { at last, the return is generated }
  764. if not inlined then
  765. if po_interrupt in aktprocsym^.definition^.procoptions then
  766. g_interrupt_stackframe_exit(list)
  767. else
  768. g_return_from_proc(list,parasize);
  769. list^.concat(new(pai_symbol_end,initname(aktprocsym^.definition^.mangledname)));
  770. {$ifdef GDB}
  771. if (cs_debuginfo in aktmoduleswitches) and not inlined then
  772. begin
  773. aktprocsym^.concatstabto(list);
  774. if assigned(procinfo^._class) then
  775. if (not assigned(procinfo^.parent) or
  776. not assigned(procinfo^.parent^._class)) then
  777. list^.concat(new(pai_stabs,init(strpnew(
  778. '"$t:v'+procinfo^._class^.numberstring+'",'+
  779. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.selfpointer_offset)))));
  780. {!!!!!!!!!!!!
  781. else
  782. list^.concat(new(pai_stabs,init(strpnew(
  783. '"$t:r'+procinfo^._class^.numberstring+'",'+
  784. tostr(N_RSYM)+',0,0,'+tostr(GDB_i386index[R_ESI])))));
  785. }
  786. if (pdef(aktprocsym^.definition^.rettype.def) <> pdef(voiddef)) then
  787. begin
  788. if ret_in_param(aktprocsym^.definition^.rettype.def) then
  789. list^.concat(new(pai_stabs,init(strpnew(
  790. '"'+aktprocsym^.name+':X*'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  791. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))))
  792. else
  793. list^.concat(new(pai_stabs,init(strpnew(
  794. '"'+aktprocsym^.name+':X'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  795. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))));
  796. if (m_result in aktmodeswitches) then
  797. if ret_in_param(aktprocsym^.definition^.rettype.def) then
  798. list^.concat(new(pai_stabs,init(strpnew(
  799. '"RESULT:X*'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  800. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))))
  801. else
  802. list^.concat(new(pai_stabs,init(strpnew(
  803. '"RESULT:X'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  804. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))));
  805. end;
  806. mangled_length:=length(aktprocsym^.definition^.mangledname);
  807. getmem(p,mangled_length+50);
  808. strpcopy(p,'192,0,0,');
  809. strpcopy(strend(p),aktprocsym^.definition^.mangledname);
  810. list^.concat(new(pai_stabn,init(strnew(p))));
  811. {list^.concat(new(pai_stabn,init(strpnew('192,0,0,'
  812. +aktprocsym^.definition^.mangledname))));
  813. p[0]:='2';p[1]:='2';p[2]:='4';
  814. strpcopy(strend(p),'_end');}
  815. freemem(p,mangled_length+50);
  816. list^.concat(new(pai_stabn,init(
  817. strpnew('224,0,0,'+aktexit2label^.name))));
  818. { strpnew('224,0,0,'
  819. +aktprocsym^.definition^.mangledname+'_end'))));}
  820. end;
  821. {$endif GDB}
  822. end;
  823. {*****************************************************************************
  824. some abstract definitions
  825. ****************************************************************************}
  826. procedure tcg.a_call_name(list : paasmoutput;const s : string;
  827. offset : longint);
  828. begin
  829. abstract;
  830. end;
  831. procedure tcg.g_stackframe_entry(list : paasmoutput;localsize : longint);
  832. begin
  833. abstract;
  834. end;
  835. procedure tcg.g_maybe_loadself(list : paasmoutput);
  836. begin
  837. abstract;
  838. end;
  839. procedure tcg.g_restore_frame_pointer(list : paasmoutput);
  840. begin
  841. abstract;
  842. end;
  843. procedure g_return_from_proc(list : paasmoutput;parasize : aword);
  844. begin
  845. abstract;
  846. end;
  847. procedure tcg.a_loadaddress_ref_reg(list : paasmoutput;const ref : treference;r : tregister);
  848. begin
  849. abstract;
  850. end;
  851. procedure tcg.g_push_exception_value_reg(list : paasmoutput;reg : tregister);
  852. begin
  853. abstract;
  854. end;
  855. procedure tcg.g_push_exception_value_const(list : paasmoutput;reg : tregister);
  856. begin
  857. abstract;
  858. end;
  859. procedure tcg.g_pop_exception_value_reg(list : paasmoutput;reg : tregister);
  860. begin
  861. abstract;
  862. end;
  863. procedure tcg.a_load_const_reg(list : paasmoutput;size : tcgsize;a : aword;register : tregister);
  864. begin
  865. abstract;
  866. end;
  867. procedure tcg.a_load_reg_ref(list : paasmoutput;size : tcgsize;register : tregister;const ref : treference);
  868. begin
  869. abstract;
  870. end;
  871. procedure tcg.a_load_ref_reg(list : paasmoutput;size : tcgsize;const ref : treference;register : tregister);
  872. begin
  873. abstract;
  874. end;
  875. procedure tcg.a_load_reg_reg(list : paasmoutput;size : tcgsize;reg1,reg2 : tregister);
  876. begin
  877. abstract;
  878. end;
  879. procedure tcg.a_cmp_reg_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  880. l : pasmlabel);
  881. begin
  882. abstract;
  883. end;
  884. procedure tcg.a_cmp_reg_reg_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : pasmlabel);
  885. begin
  886. abstract;
  887. end;
  888. procedure tcg.a_cmp_reg_ref_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg : tregister;l : pasmlabel);
  889. begin
  890. abstract;
  891. end;
  892. procedure tcg.a_cmp_ref_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  893. l : pasmlabel);
  894. begin
  895. abstract;
  896. end;
  897. procedure tcg.a_jmp_cond(list : paasmoutput;cond : TOpCmp;l: pasmlabel);
  898. begin
  899. abstract;
  900. end;
  901. procedure tcg.g_return_from_proc(list : paasmoutput;parasize : aword);
  902. begin
  903. abstract;
  904. end;
  905. procedure tcg.a_param_reg(list : paasmoutput;size : tcgsize;r : tregister;nr : longint);
  906. begin
  907. abstract;
  908. end;
  909. procedure tcg.a_paramaddr_ref(list : paasmoutput;const r : treference;nr : longint);
  910. begin
  911. abstract;
  912. end;
  913. end.
  914. {
  915. $Log$
  916. Revision 1.33 2000-01-07 01:14:53 peter
  917. * updated copyright to 2000
  918. Revision 1.32 1999/12/01 12:42:33 peter
  919. * fixed bug 698
  920. * removed some notes about unused vars
  921. Revision 1.31 1999/11/05 13:15:00 florian
  922. * some fixes to get the new cg compiling again
  923. Revision 1.30 1999/11/05 07:05:56 jonas
  924. + a_jmp_cond()
  925. Revision 1.29 1999/10/21 16:41:41 florian
  926. * problems with readln fixed: esi wasn't restored correctly when
  927. reading ordinal fields of objects futher the register allocation
  928. didn't take care of the extra register when reading ordinal values
  929. * enumerations can now be used in constant indexes of properties
  930. Revision 1.28 1999/10/12 21:20:46 florian
  931. * new codegenerator compiles again
  932. Revision 1.27 1999/09/29 11:46:20 florian
  933. * fixed bug 292 from bugs directory
  934. Revision 1.26 1999/09/14 11:16:09 florian
  935. * only small updates to work with the current compiler
  936. Revision 1.25 1999/09/03 13:09:09 jonas
  937. * fixed typo regarding scratchregs pointer
  938. Revision 1.24 1999/08/26 14:51:54 jonas
  939. * changed get_scratch_reg so it actually uses the
  940. scratch_reg_array_pointer
  941. Revision 1.23 1999/08/25 12:00:11 jonas
  942. * changed pai386, paippc and paiapha (same for tai*) to paicpu (taicpu)
  943. Revision 1.22 1999/08/18 17:05:55 florian
  944. + implemented initilizing of data for the new code generator
  945. so it should compile now simple programs
  946. Revision 1.21 1999/08/07 14:21:08 florian
  947. * some small problems fixed
  948. Revision 1.20 1999/08/06 18:05:52 florian
  949. * implemented some stuff for assignments
  950. Revision 1.19 1999/08/06 17:00:54 florian
  951. + definition of concatcopy
  952. Revision 1.18 1999/08/06 16:37:45 jonas
  953. * completed bugfix done by Florian o I wouldn't get conflicts :)
  954. Revision 1.17 1999/08/06 16:27:26 florian
  955. * for Jonas: else he will get conflicts
  956. Revision 1.16 1999/08/06 16:04:05 michael
  957. + introduced tainstruction
  958. Revision 1.15 1999/08/06 15:53:50 florian
  959. * made the alpha version compilable
  960. Revision 1.14 1999/08/06 14:15:51 florian
  961. * made the alpha version compilable
  962. Revision 1.13 1999/08/06 13:26:50 florian
  963. * more changes ...
  964. Revision 1.12 1999/08/05 17:10:56 florian
  965. * some more additions, especially procedure
  966. exit code generation
  967. Revision 1.11 1999/08/05 14:58:11 florian
  968. * some fixes for the floating point registers
  969. * more things for the new code generator
  970. Revision 1.10 1999/08/04 00:23:52 florian
  971. * renamed i386asm and i386base to cpuasm and cpubase
  972. Revision 1.9 1999/08/02 23:13:21 florian
  973. * more changes to compile for the Alpha
  974. Revision 1.8 1999/08/02 17:14:07 florian
  975. + changed the temp. generator to an object
  976. Revision 1.7 1999/08/01 23:05:55 florian
  977. * changes to compile with FPC
  978. Revision 1.6 1999/08/01 18:22:33 florian
  979. * made it again compilable
  980. Revision 1.5 1999/01/23 23:29:46 florian
  981. * first running version of the new code generator
  982. * when compiling exceptions under Linux fixed
  983. Revision 1.4 1999/01/13 22:52:36 florian
  984. + YES, finally the new code generator is compilable, but it doesn't run yet :(
  985. Revision 1.3 1998/12/26 15:20:30 florian
  986. + more changes for the new version
  987. Revision 1.2 1998/12/15 22:18:55 florian
  988. * some code added
  989. Revision 1.1 1998/12/15 16:32:58 florian
  990. + first version, derived from old routines
  991. }