aasmcpu.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. {
  2. Copyright (c) 1999-2008 by Mazen Neifer and Florian Klaempfl
  3. Contains the assembler object for the AVR
  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. unit aasmcpu;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,
  22. globtype,globals,verbose,
  23. aasmbase,aasmtai,aasmdata,aasmsym,
  24. cgbase,cgutils,cpubase,cpuinfo,
  25. ogbase;
  26. const
  27. { "mov reg,reg" source operand number }
  28. O_MOV_SOURCE = 1;
  29. { "mov reg,reg" source operand number }
  30. O_MOV_DEST = 0;
  31. maxinfolen = 5;
  32. type
  33. tinsentry = record
  34. opcode : tasmop;
  35. ops : byte;
  36. optypes : array[0..3] of longint;
  37. code : array[0..maxinfolen] of char;
  38. flags : longint;
  39. end;
  40. pinsentry=^tinsentry;
  41. taicpu = class(tai_cpu_abstract_sym)
  42. constructor op_none(op : tasmop);
  43. constructor op_reg(op : tasmop;_op1 : tregister);
  44. constructor op_const(op : tasmop;_op1 : LongInt);
  45. constructor op_ref(op : tasmop;const _op1 : treference);
  46. constructor op_reg_reg(op : tasmop;_op1,_op2 : tregister);
  47. constructor op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
  48. constructor op_reg_const(op:tasmop; _op1: tregister; _op2: LongInt);
  49. constructor op_const_reg(op:tasmop; _op1: LongInt; _op2: tregister);
  50. constructor op_ref_reg(op : tasmop;const _op1 : treference;_op2 : tregister);
  51. { this is for Jmp instructions }
  52. constructor op_cond_sym(op : tasmop;cond:TAsmCond;_op1 : tasmsymbol);
  53. constructor op_sym(op : tasmop;_op1 : tasmsymbol);
  54. constructor op_sym_ofs(op : tasmop;_op1 : tasmsymbol;_op1ofs:longint);
  55. procedure loadbool(opidx:longint;_b:boolean);
  56. { register allocation }
  57. function is_same_reg_move(regtype: Tregistertype):boolean; override;
  58. { register spilling code }
  59. function spilling_get_operation_type(opnr: longint): topertype;override;
  60. { assembler }
  61. public
  62. { the next will reset all instructions that can change in pass 2 }
  63. procedure ResetPass1;override;
  64. procedure ResetPass2;override;
  65. { function CheckIfValid:boolean;
  66. function GetString:string; }
  67. function Pass1(objdata:TObjData):longint;override;
  68. // procedure Pass2(objdata:TObjData);override;
  69. function calcsize(p:PInsEntry):shortint;
  70. private
  71. { next fields are filled in pass1, so pass2 is faster }
  72. inssize : shortint;
  73. insoffset : longint;
  74. insentry : PInsEntry;
  75. LastInsOffset : longint; { need to be public to be reset }
  76. function FindInsentry(objdata:TObjData):boolean;
  77. end;
  78. tai_align = class(tai_align_abstract)
  79. { nothing to add }
  80. end;
  81. procedure InitAsm;
  82. procedure DoneAsm;
  83. function spilling_create_load(const ref:treference;r:tregister):Taicpu;
  84. function spilling_create_store(r:tregister; const ref:treference):Taicpu;
  85. function setcondition(i : taicpu;c : tasmcond) : taicpu;
  86. { replaces cond. branches by rjmp/jmp and the inverse cond. branch if needed
  87. and transforms special instructions to valid instruction encodings }
  88. function finalizeavrcode(list : TAsmList) : Boolean;
  89. implementation
  90. {*****************************************************************************
  91. taicpu Constructors
  92. *****************************************************************************}
  93. procedure taicpu.loadbool(opidx:longint;_b:boolean);
  94. begin
  95. if opidx>=ops then
  96. ops:=opidx+1;
  97. with oper[opidx]^ do
  98. begin
  99. if typ=top_ref then
  100. dispose(ref);
  101. b:=_b;
  102. typ:=top_bool;
  103. end;
  104. end;
  105. constructor taicpu.op_none(op : tasmop);
  106. begin
  107. inherited create(op);
  108. end;
  109. constructor taicpu.op_reg(op : tasmop;_op1 : tregister);
  110. begin
  111. inherited create(op);
  112. ops:=1;
  113. loadreg(0,_op1);
  114. end;
  115. constructor taicpu.op_ref(op : tasmop;const _op1 : treference);
  116. begin
  117. inherited create(op);
  118. ops:=1;
  119. loadref(0,_op1);
  120. end;
  121. constructor taicpu.op_const(op : tasmop;_op1 : LongInt);
  122. begin
  123. inherited create(op);
  124. ops:=1;
  125. loadconst(0,_op1);
  126. end;
  127. constructor taicpu.op_reg_reg(op : tasmop;_op1,_op2 : tregister);
  128. begin
  129. inherited create(op);
  130. ops:=2;
  131. loadreg(0,_op1);
  132. loadreg(1,_op2);
  133. end;
  134. constructor taicpu.op_reg_const(op:tasmop; _op1: tregister; _op2: LongInt);
  135. begin
  136. inherited create(op);
  137. ops:=2;
  138. loadreg(0,_op1);
  139. loadconst(1,aint(_op2));
  140. end;
  141. constructor taicpu.op_const_reg(op:tasmop; _op1: LongInt; _op2: tregister);
  142. begin
  143. inherited create(op);
  144. ops:=2;
  145. loadconst(0,_op1);
  146. loadreg(1,_op2);
  147. end;
  148. constructor taicpu.op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
  149. begin
  150. inherited create(op);
  151. ops:=2;
  152. loadreg(0,_op1);
  153. loadref(1,_op2);
  154. end;
  155. constructor taicpu.op_ref_reg(op : tasmop;const _op1 : treference;_op2 : tregister);
  156. begin
  157. inherited create(op);
  158. ops:=2;
  159. loadref(0,_op1);
  160. loadreg(1,_op2);
  161. end;
  162. constructor taicpu.op_cond_sym(op : tasmop;cond:TAsmCond;_op1 : tasmsymbol);
  163. begin
  164. inherited create(op);
  165. is_jmp:=op in jmp_instructions;
  166. condition:=cond;
  167. ops:=1;
  168. loadsymbol(0,_op1,0);
  169. end;
  170. constructor taicpu.op_sym(op : tasmop;_op1 : tasmsymbol);
  171. begin
  172. inherited create(op);
  173. is_jmp:=op in jmp_instructions;
  174. ops:=1;
  175. loadsymbol(0,_op1,0);
  176. end;
  177. constructor taicpu.op_sym_ofs(op : tasmop;_op1 : tasmsymbol;_op1ofs:longint);
  178. begin
  179. inherited create(op);
  180. ops:=1;
  181. loadsymbol(0,_op1,_op1ofs);
  182. end;
  183. function taicpu.is_same_reg_move(regtype: Tregistertype):boolean;
  184. begin
  185. result:=(
  186. ((opcode in [A_MOV,A_MOVW]) and (regtype = R_INTREGISTER))
  187. ) and
  188. (ops=2) and
  189. (oper[0]^.typ=top_reg) and
  190. (oper[1]^.typ=top_reg) and
  191. (oper[0]^.reg=oper[1]^.reg);
  192. end;
  193. function taicpu.spilling_get_operation_type(opnr: longint): topertype;
  194. begin
  195. result:=operand_read;
  196. case opcode of
  197. A_CLR,A_LDD,A_LD,A_LDI,A_LDS,
  198. A_MOV,A_MOVW,A_POP:
  199. if opnr=0 then
  200. result:=operand_write;
  201. A_CP,A_CPC,A_CPI,A_PUSH,A_SBRC,A_SBRS,A_ST,A_STD,A_STS,
  202. A_MUL,A_MULS,A_MULSU,A_FMUL,A_FMULS,A_FMULSU:
  203. ;
  204. else
  205. begin
  206. if opnr=0 then
  207. result:=operand_readwrite;
  208. end;
  209. end;
  210. end;
  211. function taicpu.calcsize(p:PInsEntry):shortint;
  212. begin
  213. case opcode of
  214. A_CALL,
  215. A_JMP:
  216. result:=4;
  217. A_LDS:
  218. if (getsupreg(oper[0]^.reg)>=RS_R16) and (getsupreg(oper[0]^.reg)<=RS_R31) and
  219. (oper[1]^.val>=0) and (oper[1]^.val<=127) then
  220. result:=2
  221. else
  222. result:=4;
  223. A_STS:
  224. if (getsupreg(oper[1]^.reg)>=RS_R16) and (getsupreg(oper[1]^.reg)<=RS_R31) and
  225. (oper[0]^.val>=0) and (oper[0]^.val<=127) then
  226. result:=2
  227. else
  228. result:=4;
  229. else
  230. result:=2;
  231. end;
  232. end;
  233. procedure taicpu.ResetPass1;
  234. begin
  235. { we need to reset everything here, because the choosen insentry
  236. can be invalid for a new situation where the previously optimized
  237. insentry is not correct }
  238. InsEntry:=nil;
  239. InsSize:=0;
  240. LastInsOffset:=-1;
  241. end;
  242. procedure taicpu.ResetPass2;
  243. begin
  244. { we are here in a second pass, check if the instruction can be optimized }
  245. {
  246. if assigned(InsEntry) and
  247. ((InsEntry^.flags and IF_PASS2)<>0) then
  248. begin
  249. InsEntry:=nil;
  250. InsSize:=0;
  251. end;
  252. }
  253. LastInsOffset:=-1;
  254. end;
  255. function taicpu.FindInsentry(objdata:TObjData):boolean;
  256. begin
  257. result:=false;
  258. end;
  259. function taicpu.Pass1(objdata:TObjData):longint;
  260. begin
  261. Pass1:=0;
  262. { Save the old offset and set the new offset }
  263. InsOffset:=ObjData.CurrObjSec.Size;
  264. InsSize:=calcsize(InsEntry);
  265. { Error? }
  266. if (Insentry=nil) and (InsSize=-1) then
  267. exit;
  268. { set the file postion }
  269. current_filepos:=fileinfo;
  270. { Get InsEntry }
  271. if FindInsEntry(objdata) then
  272. begin
  273. LastInsOffset:=InsOffset;
  274. Pass1:=InsSize;
  275. exit;
  276. end;
  277. LastInsOffset:=-1;
  278. end;
  279. function spilling_create_load(const ref:treference;r:tregister):Taicpu;
  280. begin
  281. case getregtype(r) of
  282. R_INTREGISTER :
  283. if ref.offset<>0 then
  284. result:=taicpu.op_reg_ref(A_LDD,r,ref)
  285. else
  286. result:=taicpu.op_reg_ref(A_LD,r,ref);
  287. R_ADDRESSREGISTER :
  288. if ref.offset<>0 then
  289. result:=taicpu.op_reg_ref(A_LDD,r,ref)
  290. else
  291. result:=taicpu.op_reg_ref(A_LD,r,ref);
  292. else
  293. internalerror(2004010413);
  294. end;
  295. end;
  296. function spilling_create_store(r:tregister; const ref:treference):Taicpu;
  297. begin
  298. case getregtype(r) of
  299. R_INTREGISTER :
  300. if ref.offset<>0 then
  301. result:=taicpu.op_ref_reg(A_STD,ref,r)
  302. else
  303. result:=taicpu.op_ref_reg(A_ST,ref,r);
  304. R_ADDRESSREGISTER :
  305. if ref.offset<>0 then
  306. result:=taicpu.op_ref_reg(A_STD,ref,r)
  307. else
  308. result:=taicpu.op_ref_reg(A_ST,ref,r);
  309. else
  310. internalerror(2004010414);
  311. end;
  312. end;
  313. procedure InitAsm;
  314. begin
  315. end;
  316. procedure DoneAsm;
  317. begin
  318. end;
  319. function setcondition(i : taicpu;c : tasmcond) : taicpu;
  320. begin
  321. i.condition:=c;
  322. result:=i;
  323. end;
  324. function finalizeavrcode(list : TAsmList) : Boolean;
  325. var
  326. CurrOffset : longint;
  327. curtai, firstinstruction, hp: tai;
  328. again : boolean;
  329. l : tasmlabel;
  330. inasmblock, flagsallocated: Boolean;
  331. href: treference;
  332. procedure remove_instruction;
  333. var
  334. i: Integer;
  335. hp: tai;
  336. begin
  337. taicpu(firstinstruction).opcode:=A_SLEEP;
  338. for i:=0 to taicpu(firstinstruction).opercnt-1 do
  339. taicpu(firstinstruction).freeop(i);
  340. taicpu(firstinstruction).opercnt:=0;
  341. taicpu(firstinstruction).ops:=0;
  342. firstinstruction:=tai(firstinstruction.Next);
  343. while assigned(firstinstruction) do
  344. begin
  345. if firstinstruction.typ in [ait_symbol_end,ait_label] then
  346. firstinstruction:=tai(firstinstruction.Next)
  347. else
  348. begin
  349. hp:=tai(firstinstruction.Next);
  350. list.Remove(firstinstruction);
  351. firstinstruction.free;
  352. firstinstruction:=hp;
  353. end;
  354. end;
  355. end;
  356. begin
  357. again:=true;
  358. Result:=true;
  359. while again do
  360. begin
  361. again:=false;
  362. CurrOffset:=0;
  363. curtai:=tai(list.first);
  364. while assigned(curtai) do
  365. begin
  366. { instruction? }
  367. if not(curtai.typ in SkipInstr) then
  368. case curtai.typ of
  369. ait_instruction:
  370. begin
  371. taicpu(curtai).InsOffset:=CurrOffset;
  372. inc(CurrOffset,taicpu(curtai).calcsize(nil));
  373. end;
  374. ait_align:
  375. inc(CurrOffset,tai_align(curtai).aligntype);
  376. ait_const:
  377. inc(CurrOffset,tai_const(curtai).size);
  378. ait_symbolpair,
  379. ait_marker:
  380. ;
  381. ait_label:
  382. begin
  383. tai_label(curtai).labsym.offset:=CurrOffset;
  384. end;
  385. else
  386. internalerror(2011082401);
  387. end;
  388. curtai:=tai(curtai.next);
  389. end;
  390. curtai:=tai(list.first);
  391. inasmblock:=false;
  392. firstinstruction:=nil;
  393. flagsallocated:=false;
  394. while assigned(curtai) do
  395. begin
  396. case curtai.typ of
  397. ait_instruction:
  398. begin
  399. if not(assigned(firstinstruction)) then
  400. firstinstruction:=curtai;
  401. case taicpu(curtai).opcode of
  402. A_BRxx:
  403. if (taicpu(curtai).oper[0]^.typ=top_ref) and ((taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset>63*2) or
  404. (taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset<-64*2)) then
  405. begin
  406. if inasmblock then
  407. MessagePos1(taicpu(curtai).fileinfo,asmw_e_destination_out_of_range,'BR'+uppercond2str[taicpu(curtai).condition])
  408. else
  409. begin
  410. current_asmdata.getjumplabel(l);
  411. l.increfs;
  412. list.insertafter(tai_label.create(l),curtai);
  413. if CPUAVR_HAS_JMP_CALL in cpu_capabilities[current_settings.cputype] then
  414. list.insertafter(taicpu.op_sym(A_JMP,taicpu(curtai).oper[0]^.ref^.symbol),curtai)
  415. else
  416. list.insertafter(taicpu.op_sym(A_RJMP,taicpu(curtai).oper[0]^.ref^.symbol),curtai);
  417. taicpu(curtai).oper[0]^.ref^.symbol:=l;
  418. taicpu(curtai).condition:=inverse_cond(taicpu(curtai).condition);
  419. again:=true;
  420. end;
  421. end;
  422. A_RJMP:
  423. if inasmblock and (taicpu(curtai).oper[0]^.typ=top_ref) and
  424. not(taicpu(curtai).oper[0]^.ref^.symbol.typ=AT_FUNCTION) and
  425. ((taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset>2047*2) or
  426. (taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset<-2048*2)) then
  427. begin
  428. MessagePos1(taicpu(curtai).fileinfo,asmw_e_destination_out_of_range,'RJMP');
  429. end;
  430. A_JMP:
  431. { replace JMP by RJMP? ...
  432. ... but do not mess with asm block.
  433. Replacing JMP with RJMP shorten the distance to the destination
  434. in the reverse direction by 2 bytes, hence checking against a
  435. distance of -2049*2 bytes. }
  436. if not(inasmblock) and (taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset<=2047*2) and
  437. (taicpu(curtai).InsOffset-taicpu(curtai).oper[0]^.ref^.symbol.offset>=-2049*2) and
  438. { jmps to function go outside the currently considered scope, so do not mess with them.
  439. Those are generated by the peephole optimizer from call/ret sequences }
  440. not(taicpu(curtai).oper[0]^.ref^.symbol.typ=AT_FUNCTION) then
  441. begin
  442. taicpu(curtai).opcode:=A_RJMP;
  443. again:=true;
  444. end;
  445. A_STS:
  446. begin
  447. if current_settings.cputype in [cpu_avrtiny, cpu_avr1] then
  448. with taicpu(curtai).oper[0]^ do
  449. if (ref^.base=NR_NO) and (ref^.index=NR_NO) and (ref^.symbol=nil) and (ref^.offset<$40) then
  450. begin
  451. taicpu(curtai).opcode:=A_OUT;
  452. taicpu(curtai).loadconst(0,ref^.offset);
  453. end
  454. else if current_settings.cputype=cpu_avr1 then
  455. begin
  456. remove_instruction;
  457. result:=false;
  458. end;
  459. end;
  460. A_LDS:
  461. begin
  462. if current_settings.cputype in [cpu_avrtiny, cpu_avr1] then
  463. with taicpu(curtai).oper[1]^ do
  464. if (ref^.base=NR_NO) and (ref^.index=NR_NO) and (ref^.symbol=nil) and (ref^.offset<$40) then
  465. begin
  466. taicpu(curtai).opcode:=A_IN;
  467. taicpu(curtai).loadconst(1,ref^.offset)
  468. end
  469. else if current_settings.cputype=cpu_avr1 then
  470. begin
  471. remove_instruction;
  472. result:=false;
  473. end;
  474. end;
  475. A_SBIW,
  476. A_MULS,
  477. A_ICALL,
  478. A_IJMP,
  479. A_STD,
  480. A_LD,
  481. A_LDD,
  482. A_ST,
  483. A_ROR,
  484. A_POP,
  485. A_PUSH:
  486. begin
  487. { certain cpu types do not support some instructions, so replace them }
  488. if current_settings.cputype=cpu_avr1 then
  489. begin
  490. remove_instruction;
  491. result:=false;
  492. end;
  493. end;
  494. end;
  495. end;
  496. ait_regalloc:
  497. case tai_regalloc(curtai).ratype of
  498. ra_alloc:
  499. if (tai_regalloc(curtai).reg=NR_DEFAULTFLAGS) then
  500. begin
  501. { there are still douple allocations/deallocations in the cg, so
  502. this ie cannot be enabled
  503. if flagsallocated then
  504. Internalerror(2022050101);
  505. }
  506. flagsallocated:=true;
  507. end;
  508. ra_dealloc:
  509. if (tai_regalloc(curtai).reg=NR_DEFAULTFLAGS) then
  510. begin
  511. { there are still douple allocations/deallocations in the cg, so
  512. this ie cannot be enabled
  513. if not(flagsallocated) then
  514. Internalerror(2022050102);
  515. }
  516. flagsallocated:=false;
  517. end;
  518. end;
  519. ait_marker:
  520. case tai_marker(curtai).Kind of
  521. mark_AsmBlockStart:
  522. inasmblock:=true;
  523. mark_AsmBlockEnd:
  524. inasmblock:=false;
  525. mark_may_store_flags_with_r26:
  526. begin
  527. if flagsallocated then
  528. begin
  529. hp:=taicpu.op_reg_const(A_IN,NR_R26,63);
  530. list.insertafter(hp,curtai);
  531. list.insertafter(taicpu.op_reg(A_PUSH,NR_R26),hp);
  532. list.Remove(curtai);
  533. curtai.Free;
  534. curtai:=hp;
  535. end;
  536. end;
  537. mark_may_restore_flags_with_r26:
  538. begin
  539. if flagsallocated then
  540. begin
  541. hp:=taicpu.op_reg(A_POP,NR_R26);
  542. list.insertafter(hp,curtai);
  543. list.insertafter(taicpu.op_const_reg(A_OUT,63,NR_R26),hp);
  544. list.Remove(curtai);
  545. curtai.Free;
  546. curtai:=hp;
  547. end;
  548. end;
  549. end;
  550. end;
  551. curtai:=tai(curtai.next);
  552. end;
  553. end;
  554. end;
  555. begin
  556. cai_cpu:=taicpu;
  557. cai_align:=tai_align;
  558. end.