tgobj.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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;
  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: taasmoutput; size,alignment : longint; temptype : ttemptype; def:tdef) : longint;
  51. procedure freetemp(list: taasmoutput; 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: taasmoutput; size : longint;temptype:ttemptype;var ref : treference);
  70. procedure gettemptyped(list: taasmoutput; def:tdef;temptype:ttemptype;var ref : treference);
  71. procedure ungettemp(list: taasmoutput; const ref : treference);
  72. function sizeoftemp(list: taasmoutput; const ref: treference): longint;
  73. function changetemptype(list: taasmoutput; 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: taasmoutput; const ref : treference);
  84. { Allocate space for a local }
  85. procedure getlocal(list: taasmoutput; size : longint;def:tdef;var ref : treference);
  86. procedure UnGetLocal(list: taasmoutput; const ref : treference);
  87. end;
  88. var
  89. tg: ttgobj;
  90. procedure location_freetemp(list:taasmoutput; const l : tlocation);
  91. implementation
  92. uses
  93. cutils,
  94. systems,verbose,
  95. procinfo
  96. ;
  97. const
  98. FreeTempTypes = [tt_free,tt_freenoreuse];
  99. {$ifdef EXTDEBUG}
  100. TempTypeStr : array[ttemptype] of string[18] = (
  101. '<none>',
  102. 'free','normal','persistant',
  103. 'noreuse','freenoreuse'
  104. );
  105. {$endif EXTDEBUG}
  106. Used2Free : array[ttemptype] of ttemptype = (
  107. tt_none,
  108. tt_none,tt_free,tt_free,
  109. tt_freenoreuse,tt_none
  110. );
  111. {*****************************************************************************
  112. Helpers
  113. *****************************************************************************}
  114. procedure location_freetemp(list:taasmoutput; const l : tlocation);
  115. begin
  116. if (l.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
  117. tg.ungetiftemp(list,l.reference);
  118. end;
  119. {*****************************************************************************
  120. TTGOBJ
  121. *****************************************************************************}
  122. constructor ttgobj.create;
  123. begin
  124. tempfreelist:=nil;
  125. templist:=nil;
  126. { we could create a new child class for this but I don't if it is worth the effort (FK) }
  127. {$ifdef powerpc}
  128. direction:=1;
  129. {$else powerpc}
  130. {$ifdef POWERPC64}
  131. direction:=1;
  132. {$else POWERPC64}
  133. direction:=-1;
  134. {$endif POWERPC64}
  135. {$endif powerpc}
  136. end;
  137. procedure ttgobj.resettempgen;
  138. var
  139. hp : ptemprecord;
  140. {$ifdef EXTDEBUG}
  141. currpos,
  142. lastpos : longint;
  143. {$endif EXTDEBUG}
  144. begin
  145. {$ifdef EXTDEBUG}
  146. lastpos:=lasttemp;
  147. {$endif EXTDEBUG}
  148. { Clear the old templist }
  149. while assigned(templist) do
  150. begin
  151. {$ifdef EXTDEBUG}
  152. if not(templist^.temptype in FreeTempTypes) then
  153. begin
  154. Comment(V_Warning,'tgobj: (ResetTempgen) temp at pos '+tostr(templist^.pos)+
  155. ' with size '+tostr(templist^.size)+' and type '+TempTypeStr[templist^.temptype]+
  156. ' from pos '+tostr(templist^.posinfo.line)+':'+tostr(templist^.posinfo.column)+
  157. ' not freed at the end of the procedure');
  158. end;
  159. if direction=1 then
  160. currpos:=templist^.pos+templist^.size
  161. else
  162. currpos:=templist^.pos;
  163. if currpos<>lastpos then
  164. begin
  165. Comment(V_Warning,'tgobj: (ResetTempgen) temp at pos '+tostr(templist^.pos)+
  166. ' with size '+tostr(templist^.size)+' and type '+TempTypeStr[templist^.temptype]+
  167. ' from pos '+tostr(templist^.posinfo.line)+':'+tostr(templist^.posinfo.column)+
  168. ' was expected at position '+tostr(lastpos));
  169. end;
  170. if direction=1 then
  171. lastpos:=templist^.pos
  172. else
  173. lastpos:=templist^.pos+templist^.size;
  174. {$endif EXTDEBUG}
  175. hp:=templist;
  176. templist:=hp^.next;
  177. dispose(hp);
  178. end;
  179. templist:=nil;
  180. tempfreelist:=nil;
  181. firsttemp:=0;
  182. lasttemp:=0;
  183. end;
  184. procedure ttgobj.setfirsttemp(l : longint);
  185. begin
  186. { this is a negative value normally }
  187. if l*direction>=0 then
  188. begin
  189. if odd(l) then
  190. inc(l,direction);
  191. end
  192. else
  193. internalerror(200204221);
  194. firsttemp:=l;
  195. lasttemp:=l;
  196. end;
  197. function ttgobj.AllocTemp(list: taasmoutput; size,alignment : longint; temptype : ttemptype;def : tdef) : longint;
  198. var
  199. tl,htl,
  200. bestslot,bestprev,
  201. hprev,hp : ptemprecord;
  202. bestsize : longint;
  203. freetype : ttemptype;
  204. begin
  205. AllocTemp:=0;
  206. bestprev:=nil;
  207. bestslot:=nil;
  208. tl:=nil;
  209. bestsize:=0;
  210. if size=0 then
  211. begin
  212. {$ifdef EXTDEBUG}
  213. Comment(V_Warning,'tgobj: (AllocTemp) temp of size 0 requested, allocating 4 bytes');
  214. {$endif}
  215. size:=4;
  216. end;
  217. freetype:=Used2Free[temptype];
  218. if freetype=tt_none then
  219. internalerror(200208201);
  220. size:=align(size,alignment);
  221. { First check the tmpfreelist, but not when
  222. we don't want to reuse an already allocated block }
  223. if assigned(tempfreelist) and
  224. (temptype<>tt_noreuse) then
  225. begin
  226. hprev:=nil;
  227. hp:=tempfreelist;
  228. while assigned(hp) do
  229. begin
  230. {$ifdef EXTDEBUG}
  231. if not(hp^.temptype in FreeTempTypes) then
  232. Comment(V_Warning,'tgobj: (AllocTemp) temp at pos '+tostr(hp^.pos)+ ' in freelist is not set to tt_free !');
  233. {$endif}
  234. { Check only slots that are
  235. - free
  236. - share the same type
  237. - contain enough space
  238. - has a correct alignment }
  239. if (hp^.temptype=freetype) and
  240. (hp^.def=def) and
  241. (hp^.size>=size) and
  242. (hp^.pos=align(hp^.pos,alignment)) then
  243. begin
  244. { Slot is the same size then leave immediatly }
  245. if (hp^.size=size) then
  246. begin
  247. bestprev:=hprev;
  248. bestslot:=hp;
  249. bestsize:=size;
  250. break;
  251. end
  252. else
  253. begin
  254. if (bestsize=0) or (hp^.size<bestsize) then
  255. begin
  256. bestprev:=hprev;
  257. bestslot:=hp;
  258. bestsize:=hp^.size;
  259. end;
  260. end;
  261. end;
  262. hprev:=hp;
  263. hp:=hp^.nextfree;
  264. end;
  265. end;
  266. { Reuse an old temp ? }
  267. if assigned(bestslot) then
  268. begin
  269. if bestsize=size then
  270. begin
  271. tl:=bestslot;
  272. { Remove from the tempfreelist }
  273. if assigned(bestprev) then
  274. bestprev^.nextfree:=tl^.nextfree
  275. else
  276. tempfreelist:=tl^.nextfree;
  277. end
  278. else
  279. begin
  280. { Duplicate bestlost and the block in the list }
  281. new(tl);
  282. move(bestslot^,tl^,sizeof(ttemprecord));
  283. tl^.next:=bestslot^.next;
  284. bestslot^.next:=tl;
  285. { Now we split the block in 2 parts. Depending on the direction
  286. we need to resize the newly inserted block or the old reused block.
  287. For direction=1 we can use tl for the new block. For direction=-1 we
  288. will be reusing bestslot and resize the new block, that means we need
  289. to swap the pointers }
  290. if direction=-1 then
  291. begin
  292. htl:=tl;
  293. tl:=bestslot;
  294. bestslot:=htl;
  295. { Update the tempfreelist to point to the new block }
  296. if assigned(bestprev) then
  297. bestprev^.nextfree:=bestslot
  298. else
  299. tempfreelist:=bestslot;
  300. end;
  301. { Create new block and resize the old block }
  302. tl^.size:=size;
  303. tl^.nextfree:=nil;
  304. { Resize the old block }
  305. dec(bestslot^.size,size);
  306. inc(bestslot^.pos,size);
  307. end;
  308. tl^.temptype:=temptype;
  309. tl^.def:=def;
  310. tl^.nextfree:=nil;
  311. end
  312. else
  313. begin
  314. { create a new temp, we need to allocate at least a minimum of
  315. 4 bytes, else we get two temps at the same position resulting
  316. in problems when finding the corresponding temprecord }
  317. if size<4 then
  318. size:=4;
  319. { now we can create the templist entry }
  320. new(tl);
  321. tl^.temptype:=temptype;
  322. tl^.def:=def;
  323. { Extend the temp }
  324. if direction=-1 then
  325. begin
  326. lasttemp:=(-align(-lasttemp,alignment))-size;
  327. tl^.pos:=lasttemp;
  328. end
  329. else
  330. begin
  331. tl^.pos:=align(lasttemp,alignment);
  332. lasttemp:=tl^.pos+size;
  333. end;
  334. tl^.size:=size;
  335. tl^.next:=templist;
  336. tl^.nextfree:=nil;
  337. templist:=tl;
  338. end;
  339. {$ifdef EXTDEBUG}
  340. tl^.posinfo:=aktfilepos;
  341. if assigned(tl^.def) then
  342. list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]+' for def '+tl^.def.typename))
  343. else
  344. list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]));
  345. {$else}
  346. list.concat(tai_tempalloc.alloc(tl^.pos,tl^.size));
  347. {$endif}
  348. AllocTemp:=tl^.pos;
  349. end;
  350. procedure ttgobj.FreeTemp(list: taasmoutput; pos:longint;temptypes:ttemptypeset);
  351. var
  352. hp,hnext,hprev,hprevfree : ptemprecord;
  353. begin
  354. hp:=templist;
  355. hprev:=nil;
  356. hprevfree:=nil;
  357. while assigned(hp) do
  358. begin
  359. if (hp^.pos=pos) then
  360. begin
  361. { check if already freed }
  362. if hp^.temptype in FreeTempTypes then
  363. begin
  364. {$ifdef EXTDEBUG}
  365. Comment(V_Warning,'tgobj: (FreeTemp) temp at pos '+tostr(pos)+ ' is already free !');
  366. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
  367. {$endif}
  368. exit;
  369. end;
  370. { check type that are allowed to be released }
  371. if not(hp^.temptype in temptypes) then
  372. begin
  373. {$ifdef EXTDEBUG}
  374. Comment(V_Debug,'tgobj: (Freetemp) temp at pos '+tostr(pos)+ ' has different type ('+TempTypeStr[hp^.temptype]+'), not releasing');
  375. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp has wrong type ('+TempTypeStr[hp^.temptype]+') not releasing'));
  376. {$endif}
  377. exit;
  378. end;
  379. list.concat(tai_tempalloc.dealloc(hp^.pos,hp^.size));
  380. { set this block to free }
  381. hp^.temptype:=Used2Free[hp^.temptype];
  382. { Update tempfreelist }
  383. if assigned(hprevfree) then
  384. begin
  385. { Concat blocks when the previous block is free and
  386. there is no block assigned for a tdef }
  387. if assigned(hprev) and
  388. (hp^.temptype=tt_free) and
  389. not assigned(hp^.def) and
  390. (hprev^.temptype=tt_free) and
  391. not assigned(hprev^.def) then
  392. begin
  393. inc(hprev^.size,hp^.size);
  394. if direction=1 then
  395. hprev^.pos:=hp^.pos;
  396. hprev^.next:=hp^.next;
  397. dispose(hp);
  398. hp:=hprev;
  399. end
  400. else
  401. hprevfree^.nextfree:=hp;
  402. end
  403. else
  404. begin
  405. hp^.nextfree:=tempfreelist;
  406. tempfreelist:=hp;
  407. end;
  408. { Concat blocks when the next block is free and
  409. there is no block assigned for a tdef }
  410. hnext:=hp^.next;
  411. if assigned(hnext) and
  412. (hp^.temptype=tt_free) and
  413. not assigned(hp^.def) and
  414. (hnext^.temptype=tt_free) and
  415. not assigned(hnext^.def) then
  416. begin
  417. inc(hp^.size,hnext^.size);
  418. if direction=1 then
  419. hp^.pos:=hnext^.pos;
  420. hp^.nextfree:=hnext^.nextfree;
  421. hp^.next:=hnext^.next;
  422. dispose(hnext);
  423. end;
  424. { Stop }
  425. exit;
  426. end;
  427. if (hp^.temptype=tt_free) then
  428. hprevfree:=hp;
  429. hprev:=hp;
  430. hp:=hp^.next;
  431. end;
  432. end;
  433. procedure ttgobj.gettemp(list: taasmoutput; size : longint;temptype:ttemptype;var ref : treference);
  434. var
  435. varalign : longint;
  436. begin
  437. varalign:=size_2_align(size);
  438. varalign:=used_align(varalign,aktalignment.localalignmin,aktalignment.localalignmax);
  439. { can't use reference_reset_base, because that will let tgobj depend
  440. on cgobj (PFV) }
  441. fillchar(ref,sizeof(ref),0);
  442. ref.base:=current_procinfo.framepointer;
  443. ref.offset:=alloctemp(list,size,varalign,temptype,nil);
  444. end;
  445. procedure ttgobj.gettemptyped(list: taasmoutput; def:tdef;temptype:ttemptype;var ref : treference);
  446. var
  447. varalign : longint;
  448. begin
  449. varalign:=def.alignment;
  450. varalign:=used_align(varalign,aktalignment.localalignmin,aktalignment.localalignmax);
  451. { can't use reference_reset_base, because that will let tgobj depend
  452. on cgobj (PFV) }
  453. fillchar(ref,sizeof(ref),0);
  454. ref.base:=current_procinfo.framepointer;
  455. ref.offset:=alloctemp(list,def.size,varalign,temptype,def);
  456. end;
  457. function ttgobj.istemp(const ref : treference) : boolean;
  458. begin
  459. { ref.index = R_NO was missing
  460. led to problems with local arrays
  461. with lower bound > 0 (PM) }
  462. if direction = 1 then
  463. begin
  464. istemp:=(ref.base=current_procinfo.framepointer) and
  465. (ref.index=NR_NO) and
  466. (ref.offset>=firsttemp);
  467. end
  468. else
  469. begin
  470. istemp:=(ref.base=current_procinfo.framepointer) and
  471. (ref.index=NR_NO) and
  472. (ref.offset<firsttemp);
  473. end;
  474. end;
  475. function ttgobj.sizeoftemp(list: taasmoutput; const ref: treference): longint;
  476. var
  477. hp : ptemprecord;
  478. begin
  479. SizeOfTemp := -1;
  480. hp:=templist;
  481. while assigned(hp) do
  482. begin
  483. if (hp^.pos=ref.offset) then
  484. begin
  485. sizeoftemp := hp^.size;
  486. exit;
  487. end;
  488. hp := hp^.next;
  489. end;
  490. {$ifdef EXTDEBUG}
  491. comment(v_debug,'tgobj: (SizeOfTemp) temp at pos '+tostr(ref.offset)+' not found !');
  492. list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
  493. {$endif}
  494. end;
  495. function ttgobj.ChangeTempType(list: taasmoutput; const ref:treference;temptype:ttemptype):boolean;
  496. var
  497. hp : ptemprecord;
  498. begin
  499. ChangeTempType:=false;
  500. hp:=templist;
  501. while assigned(hp) do
  502. begin
  503. if (hp^.pos=ref.offset) then
  504. begin
  505. if hp^.temptype<>tt_free then
  506. begin
  507. {$ifdef EXTDEBUG}
  508. if hp^.temptype=temptype then
  509. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  510. ' at pos '+tostr(ref.offset)+ ' is already of the correct type !');
  511. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'type changed to '+TempTypeStr[temptype]));
  512. {$endif}
  513. ChangeTempType:=true;
  514. hp^.temptype:=temptype;
  515. end
  516. else
  517. begin
  518. {$ifdef EXTDEBUG}
  519. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  520. ' at pos '+tostr(ref.offset)+ ' is already freed !');
  521. list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
  522. {$endif}
  523. end;
  524. exit;
  525. end;
  526. hp:=hp^.next;
  527. end;
  528. {$ifdef EXTDEBUG}
  529. Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
  530. ' at pos '+tostr(ref.offset)+ ' not found !');
  531. list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
  532. {$endif}
  533. end;
  534. procedure ttgobj.UnGetTemp(list: taasmoutput; const ref : treference);
  535. begin
  536. FreeTemp(list,ref.offset,[tt_normal,tt_noreuse,tt_persistent]);
  537. end;
  538. procedure ttgobj.UnGetIfTemp(list: taasmoutput; const ref : treference);
  539. begin
  540. if istemp(ref) then
  541. FreeTemp(list,ref.offset,[tt_normal]);
  542. end;
  543. procedure ttgobj.getlocal(list: taasmoutput; size : longint;def:tdef;var ref : treference);
  544. var
  545. varalign : longint;
  546. begin
  547. varalign:=def.alignment;
  548. varalign:=used_align(varalign,aktalignment.localalignmin,aktalignment.localalignmax);
  549. { can't use reference_reset_base, because that will let tgobj depend
  550. on cgobj (PFV) }
  551. fillchar(ref,sizeof(ref),0);
  552. ref.base:=current_procinfo.framepointer;
  553. ref.offset:=alloctemp(list,size,varalign,tt_persistent,nil);
  554. end;
  555. procedure ttgobj.UnGetLocal(list: taasmoutput; const ref : treference);
  556. begin
  557. FreeTemp(list,ref.offset,[tt_persistent]);
  558. end;
  559. end.