tgobj.pas 21 KB

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