tgobj.pas 21 KB

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