tgen68k.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. {
  2. $Id$
  3. Copyright (c) 1993-98 by Florian Klaempfl, Carl Eric Codere
  4. This unit handles the temporary variables stuff for m68k
  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. unit tgen68k;
  19. interface
  20. uses
  21. cobjects,globals,tree,hcodegen,verbose,files,aasm
  22. {$ifdef m68k}
  23. ,m68k
  24. {$endif}
  25. ;
  26. type
  27. tregisterset = set of tregister;
  28. tpushed = array[R_D0..R_A6] of boolean;
  29. const
  30. { D2 to D5 usable as scratch registers }
  31. usablereg32 : byte = 4;
  32. { A2 to A4 usable as address registers }
  33. usableaddress: byte = 3;
  34. { FP2 to FP7 usable as FPU registers }
  35. usablefloatreg : byte = 6;
  36. function getregister32 : tregister;
  37. procedure ungetregister32(r : tregister);
  38. { return a free 32-bit address register }
  39. function getaddressreg: tregister;
  40. procedure ungetregister(r : tregister);
  41. procedure cleartempgen;
  42. { generates temporary variables }
  43. procedure resettempgen;
  44. procedure setfirsttemp(l : longint);
  45. function gettempsize : longint;
  46. function gettempofsize(size : longint) : longint;
  47. procedure gettempofsizereference(l : longint;var ref : treference);
  48. function istemp(const ref : treference) : boolean;
  49. procedure ungetiftemp(const ref : treference);
  50. function getfloatreg: tregister;
  51. { returns a free floating point register }
  52. { used in real, fpu mode, otherwise we }
  53. { must use standard register allocation }
  54. procedure del_reference(const ref : treference);
  55. procedure del_locref(const location : tlocation);
  56. { pushs and restores registers }
  57. procedure pushusedregisters(var pushed : tpushed;b : word);
  58. procedure popusedregisters(const pushed : tpushed);
  59. var
  60. unused,usableregs : tregisterset;
  61. c_usableregs : longint;
  62. usedinproc : word;
  63. { count, how much a register must be pushed if it is used as register }
  64. { variable }
  65. reg_pushes : array[R_D0..R_A6] of longint;
  66. is_reg_var : array[R_D0..R_A6] of boolean;
  67. implementation
  68. procedure pushusedregisters(var pushed : tpushed;b : word);
  69. var
  70. r : tregister;
  71. begin
  72. { the following registers can be pushed }
  73. { D0, D1, D2, D3, D4, D5, D6, D7, A0 }
  74. { A1, A2, A3, A4 }
  75. for r:=R_D2 to R_A4 do
  76. begin
  77. pushed[r]:=false;
  78. { if the register is used by the calling subroutine }
  79. if ((b and ($800 shr word(r)))<>0) then
  80. begin
  81. { and is present in use }
  82. if not(r in unused) then
  83. begin
  84. { then save it }
  85. { then save it on the stack }
  86. exprasmlist^.concat(new(pai68k,op_reg_reg(A_MOVE,S_L,r,R_SPPUSH)));
  87. { here was a big problem !!!!!}
  88. { you cannot do that for a register that is
  89. globally assigned to a var
  90. this also means that you must push it much more
  91. often, but there must be a better way
  92. maybe by putting the value back to the stack !! }
  93. if not(is_reg_var[r]) then
  94. unused:=unused+[r];
  95. pushed[r]:=true;
  96. end;
  97. end;
  98. end;
  99. end;
  100. procedure popusedregisters(const pushed : tpushed);
  101. var
  102. r : tregister;
  103. begin
  104. for r:=R_A4 downto R_D2 do
  105. if pushed[r] then
  106. begin
  107. exprasmlist^.concat(new(pai68k,op_reg_reg(A_MOVE,S_L,R_SPPULL,r)));
  108. unused:=unused-[r];
  109. end;
  110. end;
  111. procedure ungetregister(r : tregister);
  112. begin
  113. ungetregister32(r)
  114. end;
  115. procedure del_reference(const ref : treference);
  116. begin
  117. if ref.isintvalue then
  118. exit;
  119. ungetregister(ref.base);
  120. ungetregister32(ref.index);
  121. end;
  122. procedure del_locref(const location : tlocation);
  123. begin
  124. if (location.loc<>loc_mem) and (location.loc<>loc_reference) then
  125. exit;
  126. if location.reference.isintvalue then
  127. exit;
  128. ungetregister(location.reference.base);
  129. ungetregister32(location.reference.index);
  130. end;
  131. procedure ungetregister32(r : tregister);
  132. begin
  133. if r in [R_D2,R_D3,R_D4,R_D5,R_D7] then
  134. begin
  135. unused:=unused+[r];
  136. inc(usablereg32);
  137. end
  138. else
  139. if r in [R_FP2,R_FP3,R_FP4,R_FP5,R_FP6,R_FP7] then
  140. begin
  141. unused:=unused+[r];
  142. inc(usablefloatreg);
  143. end
  144. else
  145. if r in [R_A2,R_A3,R_A4,R_A6,R_SP] then
  146. begin
  147. unused:=unused+[r];
  148. inc(usableaddress);
  149. {$ifdef EXTDEBUG}
  150. end
  151. else
  152. begin
  153. if not (r in [R_NO]) then
  154. begin
  155. Comment(V_Debug,'ungetregister32() deallocation of reserved register.');
  156. end;
  157. end;
  158. {$ELSE}
  159. end;
  160. {$ENDIF}
  161. end;
  162. function getfloatreg: tregister;
  163. { returns a free floating point register }
  164. { used in real, fpu mode, otherwise we }
  165. { must use standard register allocation }
  166. var
  167. i:tregister;
  168. begin
  169. dec(usablefloatreg);
  170. if usablefloatreg = 0 then
  171. Message(cg_f_internal_error_in_getfloatreg);
  172. for i:=R_FP2 to R_FP7 do
  173. begin
  174. if i in unused then
  175. begin
  176. unused := unused-[i];
  177. getfloatreg := i;
  178. exit;
  179. end;
  180. end;
  181. { if we are here, then there was an allocation failure }
  182. Message(cg_f_internal_error_in_getfloatreg);
  183. end;
  184. function getaddressreg: tregister;
  185. begin
  186. dec(usableaddress);
  187. if R_A2 in unused then
  188. begin
  189. unused:=unused-[R_A2];
  190. usedinproc:=usedinproc or ($800 shr word(R_A2));
  191. getaddressreg:=R_A2;
  192. end
  193. else
  194. if R_A3 in unused then
  195. begin
  196. unused:=unused-[R_A3];
  197. usedinproc:=usedinproc or ($800 shr word(R_A3));
  198. getaddressreg:=R_A3;
  199. end
  200. else
  201. if R_A4 in unused then
  202. begin
  203. unused:=unused-[R_A4];
  204. usedinproc:=usedinproc or ($800 shr word(R_A4));
  205. getaddressreg:=R_A4;
  206. end
  207. else
  208. begin
  209. internalerror(10);
  210. end;
  211. end;
  212. function getregister32 : tregister;
  213. begin
  214. dec(usablereg32);
  215. if R_D2 in unused then
  216. begin
  217. unused:=unused-[R_D2];
  218. usedinproc:=usedinproc or ($800 shr word(R_D2));
  219. getregister32:=R_D2;
  220. end
  221. else if R_D3 in unused then
  222. begin
  223. unused:=unused-[R_D3];
  224. usedinproc:=usedinproc or ($800 shr word(R_D3));
  225. getregister32:=R_D3;
  226. end
  227. else if R_D4 in unused then
  228. begin
  229. unused:=unused-[R_D4];
  230. usedinproc:=usedinproc or ($800 shr word(R_D4));
  231. getregister32:=R_D4;
  232. end
  233. else if R_D5 in unused then
  234. begin
  235. unused:=unused-[R_D5];
  236. usedinproc:=usedinproc or ($800 shr word(R_D5));
  237. getregister32:=R_D5;
  238. end
  239. else if R_D7 in unused then
  240. begin
  241. unused:=unused-[R_D7];
  242. usedinproc:=usedinproc or ($800 shr word(R_D7));
  243. getregister32:=R_D7;
  244. end
  245. else
  246. begin
  247. internalerror(10);
  248. end;
  249. end;
  250. procedure cleartempgen;
  251. begin
  252. unused:=usableregs;
  253. usablereg32:=c_usableregs;
  254. end;
  255. type
  256. pfreerecord = ^tfreerecord;
  257. tfreerecord = record
  258. next : pfreerecord;
  259. pos : longint;
  260. size : longint;
  261. {$ifdef EXTDEBUG}
  262. line : longint;
  263. {$endif}
  264. end;
  265. var
  266. tmpfreelist : pfreerecord;
  267. templist : pfreerecord;
  268. lastoccupied : longint;
  269. firsttemp, maxtemp : longint;
  270. procedure resettempgen;
  271. var
  272. hp : pfreerecord;
  273. begin
  274. while assigned(tmpfreelist) do
  275. begin
  276. hp:=tmpfreelist;
  277. tmpfreelist:=hp^.next;
  278. dispose(hp);
  279. end;
  280. while assigned(templist) do
  281. begin
  282. {$ifdef EXTDEBUG}
  283. Comment(V_Warning,'temporary assignment of size '
  284. +tostr(templist^.size)+' from '+tostr(templist^.line)+
  285. +' at pos '+tostr(templist^.pos)+
  286. ' not freed at the end of the procedure');
  287. {$endif}
  288. hp:=templist;
  289. templist:=hp^.next;
  290. {$ifndef EXTDEBUG}
  291. dispose(hp);
  292. {$endif not EXTDEBUG}
  293. end;
  294. templist:=nil;
  295. tmpfreelist:=nil;
  296. firsttemp:=0;
  297. maxtemp:=0;
  298. lastoccupied:=0;
  299. end;
  300. procedure setfirsttemp(l : longint);
  301. begin
  302. if odd(l) then
  303. l:=l+1;
  304. firsttemp:=l;
  305. maxtemp := l;
  306. lastoccupied:=l;
  307. end;
  308. function gettempofsize(size : longint) : longint;
  309. var
  310. last,hp : pfreerecord;
  311. begin
  312. { this code comes from the heap management of FPC ... }
  313. if (size mod 4)<>0 then
  314. size:=size+(4-(size mod 4));
  315. if assigned(tmpfreelist) then
  316. begin
  317. last:=nil;
  318. hp:=tmpfreelist;
  319. while assigned(hp) do
  320. begin
  321. { first fit }
  322. if hp^.size>=size then
  323. begin
  324. gettempofsize:=hp^.pos;
  325. if hp^.pos-size < maxtemp then
  326. maxtemp := hp^.size-size;
  327. { the whole block is needed ? }
  328. if hp^.size>size then
  329. begin
  330. hp^.size:=hp^.size-size;
  331. hp^.pos:=hp^.pos-size;
  332. end
  333. else
  334. begin
  335. if assigned(last) then
  336. last^.next:=hp^.next
  337. else
  338. tmpfreelist:=nil;
  339. dispose(hp);
  340. end;
  341. exit;
  342. end;
  343. last:=hp;
  344. hp:=hp^.next;
  345. end;
  346. end;
  347. { nothing free is big enough : expand temp }
  348. gettempofsize:=lastoccupied-size;
  349. lastoccupied:=lastoccupied-size;
  350. if lastoccupied < maxtemp then
  351. maxtemp := lastoccupied;
  352. end;
  353. function gettempsize : longint;
  354. begin
  355. { we only push words and we want to stay on }
  356. { even stack addresses }
  357. { maxtemp is negative }
  358. if (maxtemp mod 2)<>0 then
  359. dec(maxtemp);
  360. gettempsize:=-maxtemp;
  361. end;
  362. procedure gettempofsizereference(l : longint;var ref : treference);
  363. var
  364. tl : pfreerecord;
  365. begin
  366. { do a reset, because the reference isn't used }
  367. reset_reference(ref);
  368. ref.offset:=gettempofsize(l);
  369. ref.base:=procinfo.framepointer;
  370. new(tl);
  371. tl^.pos:=ref.offset;
  372. tl^.size:=l;
  373. tl^.next:=templist;
  374. templist:=tl;
  375. {$ifdef EXTDEBUG}
  376. tl^.line:=current_module^.current_inputfile^.line_no;
  377. {$endif}
  378. end;
  379. function istemp(const ref : treference) : boolean;
  380. begin
  381. istemp:=((ref.base=procinfo.framepointer) and
  382. (ref.offset<firsttemp));
  383. end;
  384. procedure ungettemp(pos : longint;size : longint);
  385. var
  386. hp,newhp : pfreerecord;
  387. begin
  388. if (size mod 4)<>0 then
  389. size:=size+(4-(size mod 4));
  390. if size = 0 then
  391. exit;
  392. if pos<=lastoccupied then
  393. if pos=lastoccupied then
  394. begin
  395. lastoccupied:=pos+size;
  396. hp:=tmpfreelist;
  397. newhp:=nil;
  398. while assigned(hp) do
  399. begin
  400. { conneting a free block }
  401. if hp^.pos=lastoccupied then
  402. begin
  403. if assigned(newhp) then newhp^.next:=nil
  404. else tmpfreelist:=nil;
  405. lastoccupied:=lastoccupied+hp^.size;
  406. dispose(hp);
  407. break;
  408. end;
  409. newhp:=hp;
  410. hp:=hp^.next;
  411. end;
  412. end
  413. else
  414. begin
  415. {$ifdef EXTDEBUG}
  416. Comment(V_Warning,'temp managment problem : ungettemp() pos < lastoccupied !');
  417. {$endif}
  418. end
  419. else
  420. begin
  421. new(newhp);
  422. { size can be allways set }
  423. newhp^.size:=size;
  424. newhp^.pos := pos;
  425. { if there is no free list }
  426. if not assigned(tmpfreelist) then
  427. begin
  428. { then generate one }
  429. tmpfreelist:=newhp;
  430. newhp^.next:=nil;
  431. exit;
  432. end;
  433. { search the position to insert }
  434. hp:=tmpfreelist;
  435. while assigned(hp) do
  436. begin
  437. { conneting two blocks ? }
  438. if hp^.pos+hp^.size=pos then
  439. begin
  440. inc(hp^.size,size);
  441. dispose(newhp);
  442. break;
  443. end
  444. { if the end is reached, then concat }
  445. else if hp^.next=nil then
  446. begin
  447. hp^.next:=newhp;
  448. newhp^.next:=nil;
  449. break;
  450. end
  451. { falls der n„chste Zeiger gr”áer ist, dann }
  452. { Einh„ngen }
  453. else if hp^.next^.pos<=pos+size then
  454. begin
  455. { concat two blocks ? }
  456. if pos+size=hp^.next^.pos then
  457. begin
  458. newhp^.next:=hp^.next^.next;
  459. inc(newhp^.size,hp^.next^.size);
  460. dispose(hp^.next);
  461. hp^.next:=newhp;
  462. end
  463. else
  464. begin
  465. newhp^.next:=hp^.next;
  466. hp^.next:=newhp;
  467. end;
  468. break;
  469. end;
  470. hp:=hp^.next;
  471. end;
  472. end;
  473. end;
  474. procedure ungetiftemp(const ref : treference);
  475. var
  476. tl,prev : pfreerecord;
  477. begin
  478. if istemp(ref) then
  479. begin
  480. prev:=nil;
  481. tl:=templist;
  482. while assigned(tl) do
  483. begin
  484. if ref.offset=tl^.pos then
  485. begin
  486. ungettemp(ref.offset,tl^.size);
  487. if assigned(prev) then
  488. prev^.next:=tl^.next
  489. else
  490. templist:=tl^.next;
  491. dispose(tl);
  492. exit;
  493. end
  494. else
  495. begin
  496. prev:=tl;
  497. tl:=tl^.next;
  498. end;
  499. end;
  500. {$ifdef EXTDEBUG}
  501. Comment(V_Warning,'Internal: temp managment problem : '+
  502. 'temp not found for release at offset '+tostr(ref.offset));
  503. {$endIf}
  504. end;
  505. end;
  506. begin
  507. { contains both information on Address registers and data registers }
  508. { even if they are allocated separately. }
  509. usableregs:=[R_D0,R_D1,R_D2,R_D3,R_D4,R_D5,R_D6,R_D7,R_A0,R_A1,R_A2,R_A3,R_A4,
  510. R_FP0,R_FP1,R_FP2,R_FP3,R_FP4,R_FP5,R_FP6,R_FP7];
  511. c_usableregs:=4;
  512. tmpfreelist:=nil;
  513. templist:=nil;
  514. end.
  515. {
  516. $Log$
  517. Revision 1.1 1998-03-25 11:18:15 root
  518. Initial revision
  519. Revision 1.12 1998/03/22 12:45:38 florian
  520. * changes of Carl-Eric to m68k target commit:
  521. - wrong nodes because of the new string cg in intel, I had to create
  522. this under m68k also ... had to work it out to fix potential alignment
  523. problems --> this removes the crash of the m68k compiler.
  524. - added absolute addressing in m68k assembler (required for Amiga startup)
  525. - fixed alignment problems (because of byte return values, alignment
  526. would not be always valid) -- is this ok if i change the offset if odd in
  527. setfirsttemp ?? -- it seems ok...
  528. Revision 1.11 1998/03/10 04:21:15 carl
  529. * fixed extdebug problems
  530. Revision 1.10 1998/03/10 01:17:30 peter
  531. * all files have the same header
  532. * messages are fully implemented, EXTDEBUG uses Comment()
  533. + AG... files for the Assembler generation
  534. Revision 1.9 1998/03/06 00:53:00 peter
  535. * replaced all old messages from errore.msg, only ExtDebug and some
  536. Comment() calls are left
  537. * fixed options.pas
  538. Revision 1.8 1998/03/02 01:49:35 peter
  539. * renamed target_DOS to target_GO32V1
  540. + new verbose system, merged old errors and verbose units into one new
  541. verbose.pas, so errors.pas is obsolete
  542. Revision 1.7 1998/02/13 10:35:51 daniel
  543. * Made Motorola version compilable.
  544. * Fixed optimizer
  545. Revision 1.6 1998/01/11 03:40:16 carl
  546. + added fpu register allocation
  547. Revision 1.3 1997/12/09 14:13:07 carl
  548. * bugfix of free register list.
  549. Revision 1.2 1997/11/28 18:14:49 pierre
  550. working version with several bug fixes
  551. Revision 1.1.1.1 1997/11/27 08:33:03 michael
  552. FPC Compiler CVS start
  553. Pre-CVS log:
  554. + feature added
  555. - removed
  556. * bug fixed or changed
  557. History (started with version 0.9.0):
  558. 7th december 1996:
  559. * some code from Pierre Muller inserted
  560. makes the use of the stack more efficient
  561. 5th september 1997:
  562. + Converted for Motorola MC68000 output (C. E. Codere)
  563. 24nd september 1997:
  564. + Reserved register list modified. (CEC)
  565. 26 september 1997:
  566. + Converted to work with v093 (CEC)
  567. * Knowing that base is in address register, modified routines
  568. accordingly. (CEC)
  569. 27 september 1997:
  570. + pushusedregisters now pushes only non-scratch registers.
  571. 2nd october 1997:
  572. + added strict error checking when extdebug defined.
  573. 23 october 1997:
  574. - it seems that sp, and the base pointer can be freed in ungetregister,
  575. removed warning accordingly. (CEC).
  576. * bugfix of address register in usableregs set. (They were not defined...) (CEC).
  577. * other stupid bug! When I changed the register conventions, I forgot to change
  578. getaddressreg to reflect those changes!! (CEC).
  579. }