tgobj.pas 20 KB

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