tgobj.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. This unit implements the base object for temp. generator
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. {#@abstract(Temporary reference allocator unit)
  18. Temporary reference allocator unit. This unit contains
  19. all which is related to allocating temporary memory
  20. space on the stack, as required, by the code generator.
  21. }
  22. unit tgobj;
  23. {$i fpcdefs.inc}
  24. interface
  25. uses
  26. cclasses,
  27. globals,globtype,
  28. symtype,
  29. cpubase,cpuinfo,cgbase,cgutils,
  30. aasmbase,aasmtai,aasmdata;
  31. type
  32. ptemprecord = ^ttemprecord;
  33. ttemprecord = record
  34. temptype : ttemptype;
  35. pos : longint;
  36. size : longint;
  37. def : tdef;
  38. next : ptemprecord;
  39. nextfree : ptemprecord; { for faster freeblock checking }
  40. {$ifdef EXTDEBUG}
  41. posinfo,
  42. releaseposinfo : tfileposinfo;
  43. {$endif}
  44. end;
  45. {# Generates temporary variables }
  46. ttgobj = class
  47. private
  48. { contains all free temps using nextfree links }
  49. tempfreelist : ptemprecord;
  50. function alloctemp(list: TAsmList; size,alignment : longint; temptype : ttemptype; def:tdef) : longint;
  51. procedure freetemp(list: TAsmList; pos:longint;temptypes:ttemptypeset);
  52. public
  53. { contains all temps }
  54. templist : ptemprecord;
  55. { Offsets of the first/last temp }
  56. firsttemp,
  57. lasttemp : longint;
  58. direction : shortint;
  59. constructor create;
  60. {# Clear and free the complete linked list of temporary memory
  61. locations. The list is set to nil.}
  62. procedure resettempgen;
  63. {# Sets the first offset from the frame pointer or stack pointer where
  64. the temporary references will be allocated. It is to note that this
  65. value should always be negative.
  66. @param(l start offset where temps will start in stack)
  67. }
  68. procedure setfirsttemp(l : longint);
  69. procedure gettemp(list: TAsmList; size : longint;temptype:ttemptype;var ref : treference);
  70. procedure gettemptyped(list: TAsmList; def:tdef;temptype:ttemptype;var ref : treference);
  71. procedure ungettemp(list: TAsmList; const ref : treference);
  72. function sizeoftemp(list: TAsmList; const ref: treference): longint;
  73. function changetemptype(list: TAsmList; const ref:treference;temptype:ttemptype):boolean;
  74. {# Returns TRUE if the reference ref is allocated in temporary volatile memory space,
  75. otherwise returns FALSE.
  76. @param(ref reference to verify)
  77. }
  78. function istemp(const ref : treference) : boolean;
  79. {# Frees a reference @var(ref) which was allocated in the volatile temporary memory space.
  80. The freed space can later be reallocated and reused. If this reference
  81. is not in the temporary memory, it is simply not freed.
  82. }
  83. procedure ungetiftemp(list: TAsmList; const ref : treference);
  84. { Allocate space for a local }
  85. procedure getlocal(list: TAsmList; size : longint;def:tdef;var ref : treference);
  86. procedure getlocal(list: TAsmList; size : longint; alignment : shortint; def:tdef;var ref : treference);
  87. procedure UnGetLocal(list: TAsmList; const ref : treference);
  88. end;
  89. var
  90. tg: ttgobj;
  91. procedure location_freetemp(list:TAsmList; const l : tlocation);
  92. implementation
  93. uses
  94. cutils,
  95. systems,verbose,
  96. procinfo
  97. ;
  98. const
  99. FreeTempTypes = [tt_free,tt_freenoreuse];
  100. {$ifdef EXTDEBUG}
  101. TempTypeStr : array[ttemptype] of string[18] = (
  102. '<none>',
  103. 'free','normal','persistant',
  104. 'noreuse','freenoreuse'
  105. );
  106. {$endif EXTDEBUG}
  107. Used2Free : array[ttemptype] of ttemptype = (
  108. tt_none,
  109. tt_none,tt_free,tt_free,
  110. tt_freenoreuse,tt_none
  111. );
  112. {*****************************************************************************
  113. Helpers
  114. *****************************************************************************}
  115. procedure location_freetemp(list:TAsmList; const l : tlocation);
  116. begin
  117. if (l.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
  118. tg.ungetiftemp(list,l.reference);
  119. end;
  120. {*****************************************************************************
  121. TTGOBJ
  122. *****************************************************************************}
  123. constructor ttgobj.create;
  124. begin
  125. tempfreelist:=nil;
  126. templist:=nil;
  127. { we could create a new child class for this but I don't if it is worth the effort (FK) }
  128. {$ifdef powerpc}
  129. direction:=1;
  130. {$else powerpc}
  131. {$ifdef POWERPC64}
  132. direction:=1;
  133. {$else POWERPC64}
  134. direction:=-1;
  135. {$endif POWERPC64}
  136. {$endif powerpc}
  137. end;
  138. procedure ttgobj.resettempgen;
  139. var
  140. hp : ptemprecord;
  141. begin
  142. { Clear the old templist }
  143. while assigned(templist) do
  144. begin
  145. {$ifdef EXTDEBUG}
  146. if not(templist^.temptype in FreeTempTypes) then
  147. begin
  148. Comment(V_Warning,'tgobj: (ResetTempgen) temp at pos '+tostr(templist^.pos)+
  149. ' with size '+tostr(templist^.size)+' and type '+TempTypeStr[templist^.temptype]+
  150. ' from pos '+tostr(templist^.posinfo.line)+':'+tostr(templist^.posinfo.column)+
  151. ' not freed at the end of the procedure');
  152. end;
  153. {$endif EXTDEBUG}
  154. hp:=templist;
  155. templist:=hp^.next;
  156. dispose(hp);
  157. end;
  158. templist:=nil;
  159. tempfreelist:=nil;
  160. firsttemp:=0;
  161. lasttemp:=0;
  162. end;
  163. procedure ttgobj.setfirsttemp(l : longint);
  164. begin
  165. { this is a negative value normally }
  166. if l*direction>=0 then
  167. begin
  168. if odd(l) then
  169. inc(l,direction);
  170. end
  171. else
  172. internalerror(200204221);
  173. firsttemp:=l;
  174. lasttemp:=l;
  175. end;
  176. function ttgobj.AllocTemp(list: TAsmList; size,alignment : longint; temptype : ttemptype;def : tdef) : longint;
  177. var
  178. tl,htl,
  179. bestslot,bestprev,
  180. hprev,hp : ptemprecord;
  181. bestsize : longint;
  182. freetype : ttemptype;
  183. begin
  184. AllocTemp:=0;
  185. bestprev:=nil;
  186. bestslot:=nil;
  187. tl:=nil;
  188. bestsize:=0;
  189. if size=0 then
  190. begin
  191. {$ifdef EXTDEBUG}
  192. Comment(V_Warning,'tgobj: (AllocTemp) temp of size 0 requested, allocating 4 bytes');
  193. {$endif}
  194. size:=4;
  195. end;
  196. freetype:=Used2Free[temptype];
  197. if freetype=tt_none then
  198. internalerror(200208201);
  199. size:=align(size,alignment);
  200. { First check the tmpfreelist, but not when
  201. we don't want to reuse an already allocated block }
  202. if assigned(tempfreelist) and
  203. (temptype<>tt_noreuse) then
  204. begin
  205. hprev:=nil;
  206. hp:=tempfreelist;
  207. while assigned(hp) do
  208. begin
  209. {$ifdef EXTDEBUG}
  210. if not(hp^.temptype in FreeTempTypes) then
  211. Comment(V_Warning,'tgobj: (AllocTemp) temp at pos '+tostr(hp^.pos)+ ' in freelist is not set to tt_free !');
  212. {$endif}
  213. { Check only slots that are
  214. - free
  215. - share the same type
  216. - contain enough space
  217. - has a correct alignment }
  218. if (hp^.temptype=freetype) and
  219. (hp^.def=def) and
  220. (hp^.size>=size) and
  221. (hp^.pos=align(hp^.pos,alignment)) then
  222. begin
  223. { Slot is the same size then leave immediatly }
  224. if (hp^.size=size) then
  225. begin
  226. bestprev:=hprev;
  227. bestslot:=hp;
  228. bestsize:=size;
  229. break;
  230. end
  231. else
  232. begin
  233. if (bestsize=0) or (hp^.size<bestsize) then
  234. begin
  235. bestprev:=hprev;
  236. bestslot:=hp;
  237. bestsize:=hp^.size;
  238. end;
  239. end;
  240. end;
  241. hprev:=hp;
  242. hp:=hp^.nextfree;
  243. end;
  244. end;
  245. { Reuse an old temp ? }
  246. if assigned(bestslot) then
  247. begin
  248. if bestsize=size then
  249. begin
  250. tl:=bestslot;
  251. { Remove from the tempfreelist }
  252. if assigned(bestprev) then
  253. bestprev^.nextfree:=tl^.nextfree
  254. else
  255. tempfreelist:=tl^.nextfree;
  256. end
  257. else
  258. begin
  259. { Duplicate bestlost and the block in the list }
  260. new(tl);
  261. move(bestslot^,tl^,sizeof(ttemprecord));
  262. tl^.next:=bestslot^.next;
  263. bestslot^.next:=tl;
  264. { Now we split the block in 2 parts. Depending on the direction
  265. we need to resize the newly inserted block or the old reused block.
  266. For direction=1 we can use tl for the new block. For direction=-1 we
  267. will be reusing bestslot and resize the new block, that means we need
  268. to swap the pointers }
  269. if direction=-1 then
  270. begin
  271. htl:=tl;
  272. tl:=bestslot;
  273. bestslot:=htl;
  274. { Update the tempfreelist to point to the new block }
  275. if assigned(bestprev) then
  276. bestprev^.nextfree:=bestslot
  277. else
  278. tempfreelist:=bestslot;
  279. end;
  280. { Create new block and resize the old block }
  281. tl^.size:=size;
  282. tl^.nextfree:=nil;
  283. { Resize the old block }
  284. dec(bestslot^.size,size);
  285. inc(bestslot^.pos,size);
  286. end;
  287. tl^.temptype:=temptype;
  288. tl^.def:=def;
  289. tl^.nextfree:=nil;
  290. end
  291. else
  292. begin
  293. { now we can create the templist entry }
  294. new(tl);
  295. tl^.temptype:=temptype;
  296. tl^.def:=def;
  297. { Extend the temp }
  298. if direction=-1 then
  299. begin
  300. lasttemp:=(-align(-lasttemp,alignment))-size;
  301. tl^.pos:=lasttemp;
  302. end
  303. else
  304. begin
  305. tl^.pos:=align(lasttemp,alignment);
  306. lasttemp:=tl^.pos+size;
  307. end;
  308. tl^.size:=size;
  309. tl^.next:=templist;
  310. tl^.nextfree:=nil;
  311. templist:=tl;
  312. end;
  313. {$ifdef EXTDEBUG}
  314. tl^.posinfo:=aktfilepos;
  315. if assigned(tl^.def) then
  316. list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]+' for def '+tl^.def.typename))
  317. else
  318. list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]));
  319. {$else}
  320. list.concat(tai_tempalloc.alloc(tl^.pos,tl^.size));
  321. {$endif}
  322. AllocTemp:=tl^.pos;
  323. end;
  324. procedure ttgobj.FreeTemp(list: TAsmList; pos:longint;temptypes:ttemptypeset);
  325. var
  326. hp,hnext,hprev,hprevfree : ptemprecord;
  327. begin
  328. hp:=templist;
  329. hprev:=nil;
  330. hprevfree:=nil;
  331. while assigned(hp) do
  332. begin
  333. if (hp^.pos=pos) then
  334. begin
  335. { check if already freed }
  336. if hp^.temptype in FreeTempTypes then
  337. begin
  338. {$ifdef EXTDEBUG}
  339. Comment(V_Warning,'tgobj: (FreeTemp) temp at pos '+tostr(pos)+ ' is already free !');
  340. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
  341. {$endif}
  342. exit;
  343. end;
  344. { check type that are allowed to be released }
  345. if not(hp^.temptype in temptypes) then
  346. begin
  347. {$ifdef EXTDEBUG}
  348. Comment(V_Debug,'tgobj: (Freetemp) temp at pos '+tostr(pos)+ ' has different type ('+TempTypeStr[hp^.temptype]+'), not releasing');
  349. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp has wrong type ('+TempTypeStr[hp^.temptype]+') not releasing'));
  350. {$endif}
  351. exit;
  352. end;
  353. list.concat(tai_tempalloc.dealloc(hp^.pos,hp^.size));
  354. { set this block to free }
  355. hp^.temptype:=Used2Free[hp^.temptype];
  356. { Update tempfreelist }
  357. if assigned(hprevfree) then
  358. begin
  359. { Concat blocks when the previous block is free and
  360. there is no block assigned for a tdef }
  361. if assigned(hprev) and
  362. (hp^.temptype=tt_free) and
  363. not assigned(hp^.def) and
  364. (hprev^.temptype=tt_free) and
  365. not assigned(hprev^.def) then
  366. begin
  367. inc(hprev^.size,hp^.size);
  368. if direction=1 then
  369. hprev^.pos:=hp^.pos;
  370. hprev^.next:=hp^.next;
  371. dispose(hp);
  372. hp:=hprev;
  373. end
  374. else
  375. hprevfree^.nextfree:=hp;
  376. end
  377. else
  378. begin
  379. hp^.nextfree:=tempfreelist;
  380. tempfreelist:=hp;
  381. end;
  382. { Concat blocks when the next block is free and
  383. there is no block assigned for a tdef }
  384. hnext:=hp^.next;
  385. if assigned(hnext) and
  386. (hp^.temptype=tt_free) and
  387. not assigned(hp^.def) and
  388. (hnext^.temptype=tt_free) and
  389. not assigned(hnext^.def) then
  390. begin
  391. inc(hp^.size,hnext^.size);
  392. if direction=1 then
  393. hp^.pos:=hnext^.pos;
  394. hp^.nextfree:=hnext^.nextfree;
  395. hp^.next:=hnext^.next;
  396. dispose(hnext);
  397. end;
  398. { Stop }
  399. exit;
  400. end;
  401. if (hp^.temptype=tt_free) then
  402. hprevfree:=hp;
  403. hprev:=hp;
  404. hp:=hp^.next;
  405. end;
  406. end;
  407. procedure ttgobj.gettemp(list: TAsmList; size : longint;temptype:ttemptype;var ref : treference);
  408. var
  409. varalign : shortint;
  410. begin
  411. varalign:=size_2_align(size);
  412. varalign:=used_align(varalign,aktalignment.localalignmin,aktalignment.localalignmax);
  413. { can't use reference_reset_base, because that will let tgobj depend
  414. on cgobj (PFV) }
  415. fillchar(ref,sizeof(ref),0);
  416. ref.base:=current_procinfo.framepointer;
  417. ref.offset:=alloctemp(list,size,varalign,temptype,nil);
  418. end;
  419. procedure ttgobj.gettemptyped(list: TAsmList; def:tdef;temptype:ttemptype;var ref : treference);
  420. var
  421. varalign : shortint;
  422. begin
  423. varalign:=def.alignment;
  424. varalign:=used_align(varalign,aktalignment.localalignmin,aktalignment.localalignmax);
  425. { can't use reference_reset_base, because that will let tgobj depend
  426. on cgobj (PFV) }
  427. fillchar(ref,sizeof(ref),0);
  428. ref.base:=current_procinfo.framepointer;
  429. ref.offset:=alloctemp(list,def.size,varalign,temptype,def);
  430. end;
  431. function ttgobj.istemp(const ref : treference) : boolean;
  432. begin
  433. { ref.index = R_NO was missing
  434. led to problems with local arrays
  435. with lower bound > 0 (PM) }
  436. if direction = 1 then
  437. begin
  438. istemp:=(ref.base=current_procinfo.framepointer) and
  439. (ref.index=NR_NO) and
  440. (ref.offset>=firsttemp);
  441. end
  442. else
  443. begin
  444. istemp:=(ref.base=current_procinfo.framepointer) and
  445. (ref.index=NR_NO) and
  446. (ref.offset<firsttemp);
  447. end;
  448. end;
  449. function ttgobj.sizeoftemp(list: TAsmList; const ref: treference): longint;
  450. var
  451. hp : ptemprecord;
  452. begin
  453. SizeOfTemp := -1;
  454. hp:=templist;
  455. while assigned(hp) do
  456. begin
  457. if (hp^.pos=ref.offset) then
  458. begin
  459. sizeoftemp := hp^.size;
  460. exit;
  461. end;
  462. hp := hp^.next;
  463. end;
  464. {$ifdef EXTDEBUG}
  465. comment(v_debug,'tgobj: (SizeOfTemp) temp at pos '+tostr(ref.offset)+' not found !');
  466. list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
  467. {$endif}
  468. end;
  469. function ttgobj.ChangeTempType(list: TAsmList; const ref:treference;temptype:ttemptype):boolean;
  470. var
  471. hp : ptemprecord;
  472. begin
  473. ChangeTempType:=false;
  474. hp:=templist;
  475. while assigned(hp) do
  476. begin
  477. if (hp^.pos=ref.offset) then
  478. begin
  479. if hp^.temptype<>tt_free then
  480. begin
  481. {$ifdef EXTDEBUG}
  482. if hp^.temptype=temptype then
  483. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  484. ' at pos '+tostr(ref.offset)+ ' is already of the correct type !');
  485. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'type changed to '+TempTypeStr[temptype]));
  486. {$endif}
  487. ChangeTempType:=true;
  488. hp^.temptype:=temptype;
  489. end
  490. else
  491. begin
  492. {$ifdef EXTDEBUG}
  493. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  494. ' at pos '+tostr(ref.offset)+ ' is already freed !');
  495. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
  496. {$endif}
  497. end;
  498. exit;
  499. end;
  500. hp:=hp^.next;
  501. end;
  502. {$ifdef EXTDEBUG}
  503. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  504. ' at pos '+tostr(ref.offset)+ ' not found !');
  505. list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
  506. {$endif}
  507. end;
  508. procedure ttgobj.UnGetTemp(list: TAsmList; const ref : treference);
  509. begin
  510. FreeTemp(list,ref.offset,[tt_normal,tt_noreuse,tt_persistent]);
  511. end;
  512. procedure ttgobj.UnGetIfTemp(list: TAsmList; const ref : treference);
  513. begin
  514. if istemp(ref) then
  515. FreeTemp(list,ref.offset,[tt_normal]);
  516. end;
  517. procedure ttgobj.getlocal(list: TAsmList; size : longint;def:tdef;var ref : treference);
  518. begin
  519. getlocal(list, size, def.alignment, def, ref);
  520. end;
  521. procedure ttgobj.getlocal(list: TAsmList; size : longint; alignment : shortint; def:tdef;var ref : treference);
  522. begin
  523. alignment:=used_align(alignment,aktalignment.localalignmin,aktalignment.localalignmax);
  524. { can't use reference_reset_base, because that will let tgobj depend
  525. on cgobj (PFV) }
  526. fillchar(ref,sizeof(ref),0);
  527. ref.base:=current_procinfo.framepointer;
  528. ref.offset:=alloctemp(list,size,alignment,tt_persistent,nil);
  529. end;
  530. procedure ttgobj.UnGetLocal(list: TAsmList; const ref : treference);
  531. begin
  532. FreeTemp(list,ref.offset,[tt_persistent]);
  533. end;
  534. end.