tgobj.pas 22 KB

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