cgcpu.pas 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. {
  2. $Id$
  3. Copyright (c) 2003 by Florian Klaempfl
  4. Member of the Free Pascal development team
  5. This unit implements the code generator for the ARM
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. unit cgcpu;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. symtype,
  24. cgbase,cgobj,
  25. aasmbase,aasmcpu,aasmtai,
  26. cpubase,cpuinfo,node,cg64f32,cginfo;
  27. type
  28. tcgarm = class(tcg)
  29. procedure a_param_const(list : taasmoutput;size : tcgsize;a : aword;const locpara : tparalocation);override;
  30. procedure a_param_ref(list : taasmoutput;size : tcgsize;const r : treference;const locpara : tparalocation);override;
  31. procedure a_paramaddr_ref(list : taasmoutput;const r : treference;const locpara : tparalocation);override;
  32. procedure a_call_name(list : taasmoutput;const s : string);override;
  33. procedure a_call_reg(list : taasmoutput;reg: tregister); override;
  34. procedure a_call_ref(list : taasmoutput;const ref : treference);override;
  35. procedure a_op_const_reg(list : taasmoutput; Op: TOpCG; size: TCGSize; a: AWord; reg: TRegister); override;
  36. procedure a_op_reg_reg(list : taasmoutput; Op: TOpCG; size: TCGSize; src, dst: TRegister); override;
  37. procedure a_op_const_reg_reg(list: taasmoutput; op: TOpCg;
  38. size: tcgsize; a: aword; src, dst: tregister); override;
  39. procedure a_op_reg_reg_reg(list: taasmoutput; op: TOpCg;
  40. size: tcgsize; src1, src2, dst: tregister); override;
  41. { move instructions }
  42. procedure a_load_const_reg(list : taasmoutput; size: tcgsize; a : aword;reg : tregister);override;
  43. procedure a_load_reg_ref(list : taasmoutput; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);override;
  44. procedure a_load_ref_reg(list : taasmoutput; fromsize, tosize : tcgsize;const Ref : treference;reg : tregister);override;
  45. procedure a_load_reg_reg(list : taasmoutput; fromsize, tosize : tcgsize;reg1,reg2 : tregister);override;
  46. { fpu move instructions }
  47. procedure a_loadfpu_reg_reg(list: taasmoutput; size: tcgsize; reg1, reg2: tregister); override;
  48. procedure a_loadfpu_ref_reg(list: taasmoutput; size: tcgsize; const ref: treference; reg: tregister); override;
  49. procedure a_loadfpu_reg_ref(list: taasmoutput; size: tcgsize; reg: tregister; const ref: treference); override;
  50. { comparison operations }
  51. procedure a_cmp_const_reg_label(list : taasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  52. l : tasmlabel);override;
  53. procedure a_cmp_reg_reg_label(list : taasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : tasmlabel); override;
  54. procedure a_jmp_always(list : taasmoutput;l: tasmlabel); override;
  55. procedure a_jmp_flags(list : taasmoutput;const f : TResFlags;l: tasmlabel); override;
  56. procedure g_flags2reg(list: taasmoutput; size: TCgSize; const f: TResFlags; reg: TRegister); override;
  57. procedure g_copyvaluepara_openarray(list : taasmoutput;const ref, lenref:treference;elesize:integer);override;
  58. procedure g_stackframe_entry(list : taasmoutput;localsize : longint);override;
  59. procedure g_return_from_proc(list : taasmoutput;parasize : aword); override;
  60. procedure g_restore_frame_pointer(list : taasmoutput);override;
  61. procedure a_loadaddr_ref_reg(list : taasmoutput;const ref : treference;r : tregister);override;
  62. procedure g_concatcopy(list : taasmoutput;const source,dest : treference;len : aword; delsource,loadref : boolean);override;
  63. procedure g_overflowcheck(list: taasmoutput; const l: tlocation; def: tdef); override;
  64. procedure g_save_standard_registers(list : taasmoutput; usedinproc : Tsupregset);override;
  65. procedure g_restore_standard_registers(list : taasmoutput; usedinproc : Tsupregset);override;
  66. procedure g_save_all_registers(list : taasmoutput);override;
  67. procedure g_restore_all_registers(list : taasmoutput;accused,acchiused:boolean);override;
  68. procedure a_jmp_cond(list : taasmoutput;cond : TOpCmp;l: tasmlabel);
  69. procedure fixref(list : taasmoutput;var ref : treference);
  70. procedure handle_load_store(list:taasmoutput;op: tasmop;oppostfix : toppostfix;reg:tregister;ref: treference);
  71. end;
  72. tcg64farm = class(tcg64f32)
  73. procedure a_op64_reg_reg(list : taasmoutput;op:TOpCG;regsrc,regdst : tregister64);override;
  74. procedure a_op64_const_reg(list : taasmoutput;op:TOpCG;value : qword;reg : tregister64);override;
  75. procedure a_op64_const_reg_reg(list: taasmoutput;op:TOpCG;value : qword;regsrc,regdst : tregister64);override;
  76. procedure a_op64_reg_reg_reg(list: taasmoutput;op:TOpCG;regsrc1,regsrc2,regdst : tregister64);override;
  77. end;
  78. const
  79. OpCmp2AsmCond : Array[topcmp] of TAsmCond = (C_NONE,C_EQ,C_GT,
  80. C_LT,C_GE,C_LE,C_NE,C_LE,C_LT,C_GE,C_GT);
  81. function is_shifter_const(d : dword;var imm_shift : byte) : boolean;
  82. implementation
  83. uses
  84. globtype,globals,verbose,systems,cutils,symconst,symdef,symsym,rgobj,tgobj,cpupi;
  85. procedure tcgarm.a_param_const(list : taasmoutput;size : tcgsize;a : aword;const locpara : tparalocation);
  86. var
  87. ref: treference;
  88. begin
  89. case locpara.loc of
  90. LOC_REGISTER,LOC_CREGISTER:
  91. a_load_const_reg(list,size,a,locpara.register);
  92. LOC_REFERENCE:
  93. begin
  94. reference_reset(ref);
  95. ref.base:=locpara.reference.index;
  96. ref.offset:=locpara.reference.offset;
  97. a_load_const_ref(list,size,a,ref);
  98. end;
  99. else
  100. internalerror(2002081101);
  101. end;
  102. if locpara.sp_fixup<>0 then
  103. internalerror(2002081102);
  104. end;
  105. procedure tcgarm.a_param_ref(list : taasmoutput;size : tcgsize;const r : treference;const locpara : tparalocation);
  106. var
  107. ref: treference;
  108. tmpreg: tregister;
  109. begin
  110. case locpara.loc of
  111. LOC_REGISTER,LOC_CREGISTER:
  112. a_load_ref_reg(list,size,size,r,locpara.register);
  113. LOC_REFERENCE:
  114. begin
  115. reference_reset(ref);
  116. ref.base:=locpara.reference.index;
  117. ref.offset:=locpara.reference.offset;
  118. tmpreg := rg.getregisterint(list,size);
  119. a_load_ref_reg(list,size,size,r,tmpreg);
  120. a_load_reg_ref(list,size,size,tmpreg,ref);
  121. rg.ungetregisterint(list,tmpreg);
  122. end;
  123. LOC_FPUREGISTER,LOC_CFPUREGISTER:
  124. case size of
  125. OS_F32, OS_F64:
  126. a_loadfpu_ref_reg(list,size,r,locpara.register);
  127. else
  128. internalerror(2002072801);
  129. end;
  130. else
  131. internalerror(2002081103);
  132. end;
  133. if locpara.sp_fixup<>0 then
  134. internalerror(2002081104);
  135. end;
  136. procedure tcgarm.a_paramaddr_ref(list : taasmoutput;const r : treference;const locpara : tparalocation);
  137. var
  138. ref: treference;
  139. tmpreg: tregister;
  140. begin
  141. case locpara.loc of
  142. LOC_REGISTER,LOC_CREGISTER:
  143. a_loadaddr_ref_reg(list,r,locpara.register);
  144. LOC_REFERENCE:
  145. begin
  146. reference_reset(ref);
  147. ref.base := locpara.reference.index;
  148. ref.offset := locpara.reference.offset;
  149. tmpreg := rg.getregisterint(list,OS_ADDR);
  150. a_loadaddr_ref_reg(list,r,tmpreg);
  151. a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref);
  152. rg.ungetregisterint(list,tmpreg);
  153. end;
  154. else
  155. internalerror(2002080701);
  156. end;
  157. end;
  158. procedure tcgarm.a_call_name(list : taasmoutput;const s : string);
  159. begin
  160. list.concat(taicpu.op_sym(A_BL,objectlibrary.newasmsymbol(s)));
  161. if not(pi_do_call in current_procinfo.flags) then
  162. internalerror(2003060703);
  163. end;
  164. procedure tcgarm.a_call_reg(list : taasmoutput;reg: tregister);
  165. var
  166. r : tregister;
  167. begin
  168. r.enum:=R_INTREGISTER;
  169. r.number:=NR_PC;
  170. list.concat(taicpu.op_reg_reg(A_MOV,r,reg));
  171. if not(pi_do_call in current_procinfo.flags) then
  172. internalerror(2003060704);
  173. end;
  174. procedure tcgarm.a_call_ref(list : taasmoutput;const ref : treference);
  175. var
  176. r : tregister;
  177. begin
  178. r.enum:=R_INTREGISTER;
  179. r.number:=NR_PC;
  180. a_load_ref_reg(list,OS_ADDR,OS_ADDR,ref,r);
  181. if not(pi_do_call in current_procinfo.flags) then
  182. internalerror(2003060705);
  183. end;
  184. procedure tcgarm.a_op_const_reg(list : taasmoutput; Op: TOpCG; size: TCGSize; a: AWord; reg: TRegister);
  185. begin
  186. a_op_const_reg_reg(list,op,size,a,reg,reg);
  187. end;
  188. procedure tcgarm.a_op_reg_reg(list : taasmoutput; Op: TOpCG; size: TCGSize; src, dst: TRegister);
  189. begin
  190. case op of
  191. OP_NEG:
  192. list.concat(taicpu.op_reg_reg_const(A_RSB,dst,src,0));
  193. OP_NOT:
  194. list.concat(taicpu.op_reg_reg(A_MVN,dst,src));
  195. else
  196. a_op_reg_reg_reg(list,op,OS_32,src,dst,dst);
  197. end;
  198. end;
  199. const
  200. op_reg_reg_opcg2asmop: array[TOpCG] of tasmop =
  201. (A_NONE,A_ADD,A_AND,A_NONE,A_NONE,A_MUL,A_MUL,A_NONE,A_NONE,A_ORR,
  202. A_NONE,A_NONE,A_NONE,A_SUB,A_EOR);
  203. procedure tcgarm.a_op_const_reg_reg(list: taasmoutput; op: TOpCg;
  204. size: tcgsize; a: aword; src, dst: tregister);
  205. var
  206. shift : byte;
  207. tmpreg : tregister;
  208. so : tshifterop;
  209. begin
  210. if is_shifter_const(a,shift) and (not(op in [OP_IMUL,OP_MUL])) then
  211. case op of
  212. OP_NEG,OP_NOT,
  213. OP_DIV,OP_IDIV:
  214. internalerror(200308281);
  215. OP_SHL:
  216. begin
  217. if a>32 then
  218. internalerror(200308291);
  219. shifterop_reset(so);
  220. so.shiftmode:=SM_LSL;
  221. so.shiftimm:=a;
  222. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src,so));
  223. end;
  224. OP_SHR:
  225. begin
  226. if a>32 then
  227. internalerror(200308292);
  228. shifterop_reset(so);
  229. so.shiftmode:=SM_LSR;
  230. so.shiftimm:=a;
  231. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src,so));
  232. end;
  233. OP_SAR:
  234. begin
  235. if a>32 then
  236. internalerror(200308291);
  237. shifterop_reset(so);
  238. so.shiftmode:=SM_LSL;
  239. so.shiftimm:=a;
  240. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src,so));
  241. end;
  242. else
  243. list.concat(taicpu.op_reg_reg_const(op_reg_reg_opcg2asmop[op],dst,src,a));
  244. end
  245. else
  246. begin
  247. { there could be added some more sophisticated optimizations }
  248. if (op in [OP_MUL,OP_IMUL]) and (a=1) then
  249. a_load_reg_reg(list,size,size,src,dst)
  250. else if (op in [OP_MUL,OP_IMUL]) and (a=0) then
  251. a_load_const_reg(list,size,0,dst)
  252. else if (op in [OP_IMUL]) and (a=-1) then
  253. a_op_reg_reg(list,OP_NEG,size,src,dst)
  254. else
  255. begin
  256. tmpreg := rg.getregisterint(list,size);
  257. a_load_const_reg(list,size,a,tmpreg);
  258. a_op_reg_reg_reg(list,op,size,tmpreg,src,dst);
  259. rg.ungetregisterint(list,tmpreg);
  260. end;
  261. end;
  262. end;
  263. procedure tcgarm.a_op_reg_reg_reg(list: taasmoutput; op: TOpCg;
  264. size: tcgsize; src1, src2, dst: tregister);
  265. var
  266. so : tshifterop;
  267. tmpreg : tregister;
  268. begin
  269. case op of
  270. OP_NEG,OP_NOT,
  271. OP_DIV,OP_IDIV:
  272. internalerror(200308281);
  273. OP_SHL:
  274. begin
  275. shifterop_reset(so);
  276. so.rs:=src1;
  277. so.shiftmode:=SM_LSL;
  278. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src2,so));
  279. end;
  280. OP_SHR:
  281. begin
  282. shifterop_reset(so);
  283. so.rs:=src1;
  284. so.shiftmode:=SM_LSR;
  285. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src2,so));
  286. end;
  287. OP_SAR:
  288. begin
  289. shifterop_reset(so);
  290. so.rs:=src1;
  291. so.shiftmode:=SM_ASR;
  292. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,dst,src2,so));
  293. end;
  294. OP_IMUL,
  295. OP_MUL:
  296. begin
  297. { the arm doesn't allow that rd and rm are the same }
  298. if dst.number=src2.number then
  299. begin
  300. if src1.number<>src2.number then
  301. list.concat(taicpu.op_reg_reg_reg(A_MUL,dst,src1,src2))
  302. else
  303. begin
  304. writeln('Warning: Fix MUL');
  305. list.concat(taicpu.op_reg_reg_reg(A_MUL,dst,src2,src1));
  306. end;
  307. end
  308. else
  309. list.concat(taicpu.op_reg_reg_reg(A_MUL,dst,src2,src1));
  310. end;
  311. else
  312. list.concat(taicpu.op_reg_reg_reg(op_reg_reg_opcg2asmop[op],dst,src2,src1));
  313. end;
  314. end;
  315. function rotl(d : dword;b : byte) : dword;
  316. begin
  317. result:=(d shr (32-b)) or (d shl b);
  318. end;
  319. function is_shifter_const(d : dword;var imm_shift : byte) : boolean;
  320. var
  321. i : longint;
  322. begin
  323. for i:=0 to 15 do
  324. begin
  325. if (d and not(rotl($ff,i*2)))=0 then
  326. begin
  327. imm_shift:=i*2;
  328. result:=true;
  329. exit;
  330. end;
  331. end;
  332. result:=false;
  333. end;
  334. procedure tcgarm.a_load_const_reg(list : taasmoutput; size: tcgsize; a : aword;reg : tregister);
  335. var
  336. imm_shift : byte;
  337. l : tasmlabel;
  338. hr : treference;
  339. begin
  340. if not(size in [OS_8,OS_S8,OS_16,OS_S16,OS_32,OS_S32]) then
  341. internalerror(2002090902);
  342. if is_shifter_const(a,imm_shift) then
  343. list.concat(taicpu.op_reg_const(A_MOV,reg,a))
  344. else if is_shifter_const(not(a),imm_shift) then
  345. list.concat(taicpu.op_reg_const(A_MVN,reg,not(a)))
  346. else
  347. begin
  348. objectlibrary.getdatalabel(l);
  349. current_procinfo.aktlocaldata.concat(tai_symbol.Create(l,0));
  350. current_procinfo.aktlocaldata.concat(tai_const.Create_32bit(a));
  351. reference_reset(hr);
  352. hr.symbol:=l;
  353. list.concat(taicpu.op_reg_ref(A_LDR,reg,hr));
  354. end;
  355. end;
  356. procedure tcgarm.handle_load_store(list:taasmoutput;op: tasmop;oppostfix : toppostfix;reg:tregister;ref: treference);
  357. var
  358. tmpreg : tregister;
  359. tmpref : treference;
  360. l : tasmlabel;
  361. begin
  362. tmpreg.enum:=R_INTREGISTER;
  363. tmpreg.number:=NR_NO;
  364. { Be sure to have a base register }
  365. if (ref.base.number=NR_NO) then
  366. begin
  367. if ref.shiftmode<>SM_None then
  368. internalerror(200308294);
  369. ref.base:=ref.index;
  370. ref.index.number:=NR_NO;
  371. end;
  372. { absolute symbols can't be handled directly, we've to store the symbol reference
  373. in the text segment and access it pc relative
  374. For now, we assume that references where base or index equals to PC are already
  375. relative, all other references are assumed to be absolute and thus they need
  376. to be handled extra.
  377. A proper solution would be to change refoptions to a set and store the information
  378. if the symbol is absolute or relative there.
  379. }
  380. if (assigned(ref.symbol) and
  381. not(is_pc(ref.base)) and
  382. not(is_pc(ref.index))
  383. ) or
  384. (ref.offset<-4095) or
  385. (ref.offset>4095) then
  386. begin
  387. { check consts distance }
  388. { create consts entry }
  389. objectlibrary.getdatalabel(l);
  390. current_procinfo.aktlocaldata.concat(Tai_symbol.Create(l,0));
  391. if assigned(ref.symbol) then
  392. current_procinfo.aktlocaldata.concat(tai_const_symbol.Create_offset(ref.symbol,ref.offset))
  393. else
  394. current_procinfo.aktlocaldata.concat(tai_const.Create_32bit(ref.offset));
  395. { load consts entry }
  396. tmpreg:=rg.getregisterint(list,OS_INT);
  397. reference_reset(tmpref);
  398. tmpref.symbol:=l;
  399. tmpref.base.enum:=R_INTREGISTER;
  400. tmpref.base.number:=NR_R15;
  401. list.concat(taicpu.op_reg_ref(A_LDR,tmpreg,tmpref));
  402. if (ref.base.number<>NR_NO) then
  403. begin
  404. if ref.index.number<>NR_NO then
  405. begin
  406. list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,ref.base,tmpreg));
  407. rg.ungetregister(list,ref.base);
  408. ref.base:=tmpreg;
  409. end
  410. else
  411. begin
  412. ref.index:=tmpreg;
  413. ref.shiftimm:=0;
  414. ref.signindex:=1;
  415. ref.shiftmode:=SM_None;
  416. end;
  417. end
  418. else
  419. ref.base:=tmpreg;
  420. ref.offset:=0;
  421. ref.symbol:=nil;
  422. end;
  423. list.concat(setoppostfix(taicpu.op_reg_ref(op,reg,ref),oppostfix));
  424. if (tmpreg.number<>NR_NO) then
  425. rg.ungetregisterint(list,tmpreg);
  426. end;
  427. procedure tcgarm.a_load_reg_ref(list : taasmoutput; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);
  428. var
  429. oppostfix:toppostfix;
  430. begin
  431. case ToSize of
  432. { signed integer registers }
  433. OS_8,
  434. OS_S8:
  435. oppostfix:=PF_B;
  436. OS_16,
  437. OS_S16:
  438. oppostfix:=PF_H;
  439. OS_32,
  440. OS_S32:
  441. oppostfix:=PF_None;
  442. else
  443. InternalError(200308295);
  444. end;
  445. handle_load_store(list,A_STR,oppostfix,reg,ref);
  446. end;
  447. procedure tcgarm.a_load_ref_reg(list : taasmoutput; fromsize, tosize : tcgsize;const Ref : treference;reg : tregister);
  448. var
  449. oppostfix:toppostfix;
  450. begin
  451. case FromSize of
  452. { signed integer registers }
  453. OS_8:
  454. oppostfix:=PF_B;
  455. OS_S8:
  456. oppostfix:=PF_SB;
  457. OS_16:
  458. oppostfix:=PF_H;
  459. OS_S16:
  460. oppostfix:=PF_SH;
  461. OS_32,
  462. OS_S32:
  463. oppostfix:=PF_None;
  464. else
  465. InternalError(200308291);
  466. end;
  467. handle_load_store(list,A_LDR,oppostfix,reg,ref);
  468. end;
  469. procedure tcgarm.a_load_reg_reg(list : taasmoutput; fromsize, tosize : tcgsize;reg1,reg2 : tregister);
  470. var
  471. instr: taicpu;
  472. so : tshifterop;
  473. begin
  474. shifterop_reset(so);
  475. if (reg1.enum<>R_INTREGISTER) or (reg1.number = 0) then
  476. internalerror(200303101);
  477. if (reg2.enum<>R_INTREGISTER) or (reg2.number = 0) then
  478. internalerror(200303102);
  479. if (reg1.number<>reg2.number) or
  480. (tcgsize2size[tosize] < tcgsize2size[fromsize]) or
  481. ((tcgsize2size[tosize] = tcgsize2size[fromsize]) and
  482. (tosize <> fromsize) and
  483. not(fromsize in [OS_32,OS_S32])) then
  484. begin
  485. case tosize of
  486. OS_8:
  487. instr := taicpu.op_reg_reg_const(A_AND,
  488. reg2,reg1,$ff);
  489. OS_S8:
  490. begin
  491. so.shiftmode:=SM_LSL;
  492. so.shiftimm:=24;
  493. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg1,so));
  494. so.shiftmode:=SM_ASR;
  495. so.shiftimm:=24;
  496. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg2,so));
  497. end;
  498. OS_16:
  499. begin
  500. so.shiftmode:=SM_LSL;
  501. so.shiftimm:=16;
  502. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg1,so));
  503. so.shiftmode:=SM_LSR;
  504. so.shiftimm:=16;
  505. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg2,so));
  506. end;
  507. OS_S16:
  508. begin
  509. so.shiftmode:=SM_LSL;
  510. so.shiftimm:=16;
  511. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg1,so));
  512. so.shiftmode:=SM_ASR;
  513. so.shiftimm:=16;
  514. list.concat(taicpu.op_reg_reg_shifterop(A_MOV,reg2,reg2,so));
  515. end;
  516. OS_32,OS_S32:
  517. begin
  518. instr:=taicpu.op_reg_reg(A_MOV,reg2,reg1);
  519. rg.add_move_instruction(instr);
  520. list.concat(instr);
  521. end;
  522. else internalerror(2002090901);
  523. end;
  524. end;
  525. end;
  526. procedure tcgarm.a_loadfpu_reg_reg(list: taasmoutput; size: tcgsize; reg1, reg2: tregister);
  527. begin
  528. list.concat(setoppostfix(taicpu.op_reg_reg(A_MVF,reg2,reg1),cgsize2fpuoppostfix[size]));
  529. end;
  530. procedure tcgarm.a_loadfpu_ref_reg(list: taasmoutput; size: tcgsize; const ref: treference; reg: tregister);
  531. var
  532. oppostfix:toppostfix;
  533. begin
  534. case size of
  535. OS_F32:
  536. oppostfix:=PF_S;
  537. OS_F64:
  538. oppostfix:=PF_D;
  539. OS_F80:
  540. oppostfix:=PF_E;
  541. else
  542. InternalError(200309021);
  543. end;
  544. handle_load_store(list,A_LDF,oppostfix,reg,ref);
  545. end;
  546. procedure tcgarm.a_loadfpu_reg_ref(list: taasmoutput; size: tcgsize; reg: tregister; const ref: treference);
  547. var
  548. oppostfix:toppostfix;
  549. begin
  550. case size of
  551. OS_F32:
  552. oppostfix:=PF_S;
  553. OS_F64:
  554. oppostfix:=PF_D;
  555. OS_F80:
  556. oppostfix:=PF_E;
  557. else
  558. InternalError(200309021);
  559. end;
  560. handle_load_store(list,A_STF,oppostfix,reg,ref);
  561. end;
  562. { comparison operations }
  563. procedure tcgarm.a_cmp_const_reg_label(list : taasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  564. l : tasmlabel);
  565. var
  566. tmpreg : tregister;
  567. b : byte;
  568. begin
  569. if reg.enum=R_INTREGISTER then
  570. begin
  571. if is_shifter_const(a,b) then
  572. list.concat(taicpu.op_reg_const(A_CMN,reg,a))
  573. { CMN reg,0 and CMN reg,$80000000 are different from CMP reg,$ffffffff
  574. and CMP reg,$7fffffff regarding the flags according to the ARM manual }
  575. else if is_shifter_const(not(a),b) and (a<>$7fffffff) and (a<>$ffffffff) then
  576. list.concat(taicpu.op_reg_const(A_CMN,reg,not(a)))
  577. else
  578. begin
  579. tmpreg:=rg.getregisterint(list,size);
  580. a_load_const_reg(list,size,a,tmpreg);
  581. list.concat(taicpu.op_reg_reg(A_CMP,reg,tmpreg));
  582. rg.ungetregisterint(list,tmpreg);
  583. end
  584. end
  585. else
  586. internalerror(200308131);
  587. a_jmp_cond(list,cmp_op,l);
  588. end;
  589. procedure tcgarm.a_cmp_reg_reg_label(list : taasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : tasmlabel);
  590. begin
  591. list.concat(taicpu.op_reg_reg(A_CMP,reg2,reg1));
  592. a_jmp_cond(list,cmp_op,l);
  593. end;
  594. procedure tcgarm.a_jmp_always(list : taasmoutput;l: tasmlabel);
  595. begin
  596. list.concat(taicpu.op_sym(A_B,objectlibrary.newasmsymbol(l.name)));
  597. end;
  598. procedure tcgarm.a_jmp_flags(list : taasmoutput;const f : TResFlags;l: tasmlabel);
  599. var
  600. ai : taicpu;
  601. begin
  602. ai := Taicpu.op_sym(A_B,l);
  603. ai.SetCondition(flags_to_cond(f));
  604. ai.is_jmp := true;
  605. list.concat(ai);
  606. end;
  607. procedure tcgarm.g_flags2reg(list: taasmoutput; size: TCgSize; const f: TResFlags; reg: TRegister);
  608. var
  609. ai : taicpu;
  610. begin
  611. ai:=Taicpu.op_reg_const(A_MOV,reg,1);
  612. ai.setcondition(flags_to_cond(f));
  613. list.concat(ai);
  614. ai:=Taicpu.op_reg_const(A_MOV,reg,0);
  615. ai.setcondition(inverse_cond[flags_to_cond(f)]);
  616. list.concat(ai);
  617. end;
  618. procedure tcgarm.g_copyvaluepara_openarray(list : taasmoutput;const ref, lenref:treference;elesize:integer);
  619. begin
  620. end;
  621. procedure tcgarm.g_stackframe_entry(list : taasmoutput;localsize : longint);
  622. var
  623. rip,rsp,rfp : tregister;
  624. begin
  625. LocalSize:=align(LocalSize,4);
  626. rsp.enum:=R_INTREGISTER;
  627. rsp.number:=NR_STACK_POINTER_REG;
  628. a_reg_alloc(list,rsp);
  629. rfp.enum:=R_INTREGISTER;
  630. rfp.number:=NR_FRAME_POINTER_REG;
  631. a_reg_alloc(list,rfp);
  632. rip.enum:=R_INTREGISTER;
  633. rip.number:=NR_R12;
  634. a_reg_alloc(list,rip);
  635. list.concat(taicpu.op_reg_reg(A_MOV,rip,rsp));
  636. { restore int registers and return }
  637. list.concat(setoppostfix(taicpu.op_reg_regset(A_STM,rsp,rg.used_in_proc_int-[RS_R0..RS_R3]+[RS_R11,RS_R12,RS_R15]),PF_FD));
  638. list.concat(taicpu.op_reg_reg_const(A_SUB,rfp,rip,4));
  639. a_reg_alloc(list,rip);
  640. { allocate necessary stack size }
  641. list.concat(taicpu.op_reg_reg_const(A_SUB,rsp,rsp,LocalSize));
  642. end;
  643. procedure tcgarm.g_return_from_proc(list : taasmoutput;parasize : aword);
  644. var
  645. r1,r2 : tregister;
  646. begin
  647. if (current_procinfo.framepointer.number=NR_STACK_POINTER_REG) then
  648. begin
  649. r1.enum:=R_INTREGISTER;
  650. r1.number:=NR_R15;
  651. r2.enum:=R_INTREGISTER;
  652. r2.number:=NR_R14;
  653. list.concat(taicpu.op_reg_reg(A_MOV,r1,r2));
  654. end
  655. else
  656. begin
  657. r1.enum:=R_INTREGISTER;
  658. r1.number:=NR_R11;
  659. { restore int registers and return }
  660. list.concat(setoppostfix(taicpu.op_reg_regset(A_LDM,r1,rg.used_in_proc_int-[RS_R0..RS_R3]+[RS_R11,RS_R13,RS_R15]),PF_EA));
  661. end;
  662. end;
  663. procedure tcgarm.g_restore_frame_pointer(list : taasmoutput);
  664. begin
  665. { the frame pointer on the ARM is restored while the ret is executed }
  666. end;
  667. procedure tcgarm.a_loadaddr_ref_reg(list : taasmoutput;const ref : treference;r : tregister);
  668. var
  669. b : byte;
  670. tmpref : treference;
  671. begin
  672. tmpref:=ref;
  673. { Be sure to have a base register }
  674. if (tmpref.base.number=NR_NO) then
  675. begin
  676. if tmpref.shiftmode<>SM_None then
  677. internalerror(200308294);
  678. tmpref.base:=tmpref.index;
  679. tmpref.index.number:=NR_NO;
  680. end;
  681. if assigned(tmpref.symbol) or
  682. not(is_shifter_const(tmpref.offset,b)) or
  683. ((tmpref.base.number<>NR_NO) and tmpref.index.number<>NR_NO)) then
  684. fixref(list,tmpref);
  685. if ref.index.number<>NR_NO then
  686. begin
  687. end
  688. else
  689. list.concat(taicpu.op_reg_reg(A_MOV,r,));
  690. ref.signindex<0 then
  691. end;
  692. procedure tcgarm.fixref(list : taasmoutput;var ref : treference);
  693. var
  694. tmpreg : tregister;
  695. tmpref : treference;
  696. l : tasmlabel;
  697. begin
  698. { absolute symbols can't be handled directly, we've to store the symbol reference
  699. in the text segment and access it pc relative
  700. For now, we assume that references where base or index equals to PC are already
  701. relative, all other references are assumed to be absolute and thus they need
  702. to be handled extra.
  703. A proper solution would be to change refoptions to a set and store the information
  704. if the symbol is absolute or relative there.
  705. }
  706. { check consts distance }
  707. {!!!!!}
  708. { create consts entry }
  709. objectlibrary.getdatalabel(l);
  710. current_procinfo.aktlocaldata.concat(Tai_symbol.Create(l,0));
  711. if assigned(ref.symbol) then
  712. current_procinfo.aktlocaldata.concat(tai_const_symbol.Create_offset(ref.symbol,ref.offset))
  713. else
  714. current_procinfo.aktlocaldata.concat(tai_const.Create_32bit(ref.offset));
  715. { load consts entry }
  716. tmpreg:=rg.getregisterint(list,OS_INT);
  717. reference_reset(tmpref);
  718. tmpref.symbol:=l;
  719. tmpref.base.enum:=R_INTREGISTER;
  720. tmpref.base.number:=NR_R15;
  721. list.concat(taicpu.op_reg_ref(A_LDR,tmpreg,tmpref));
  722. if (ref.base.number<>NR_NO) then
  723. begin
  724. if ref.index.number<>NR_NO then
  725. begin
  726. list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,ref.base,tmpreg));
  727. rg.ungetregister(list,ref.base);
  728. ref.base:=tmpreg;
  729. end
  730. else
  731. begin
  732. ref.index:=tmpreg;
  733. ref.shiftimm:=0;
  734. ref.signindex:=1;
  735. ref.shiftmode:=SM_None;
  736. end;
  737. end
  738. else
  739. ref.base:=tmpreg;
  740. ref.offset:=0;
  741. ref.symbol:=nil;
  742. end;
  743. procedure tcgarm.g_concatcopy(list : taasmoutput;const source,dest : treference;len : aword; delsource,loadref : boolean);
  744. var
  745. srcref,dstref:treference;
  746. srcreg,destreg,countreg,r:tregister;
  747. helpsize:aword;
  748. copysize:byte;
  749. cgsize:Tcgsize;
  750. procedure genloop(count : aword;size : byte);
  751. const
  752. size2opsize : array[1..4] of tcgsize = (OS_8,OS_16,OS_NO,OS_32);
  753. var
  754. l : tasmlabel;
  755. begin
  756. objectlibrary.getdatalabel(l);
  757. a_load_const_reg(list,OS_INT,count,countreg);
  758. list.concat(Tai_symbol.Create(l,0));
  759. srcref.addressmode:=AM_POSTINDEXED;
  760. dstref.addressmode:=AM_POSTINDEXED;
  761. srcref.offset:=size;
  762. dstref.offset:=size;
  763. r:=rg.getregisterint(list,size2opsize[size]);
  764. a_load_ref_reg(list,size2opsize[size],size2opsize[size],srcref,r);
  765. a_load_reg_ref(list,size2opsize[size],size2opsize[size],r,dstref);
  766. rg.ungetregisterint(list,r);
  767. list.concat(setoppostfix(taicpu.op_reg_reg_const(A_SUB,countreg,countreg,1),PF_S));
  768. list.concat(setcondition(taicpu.op_sym(A_B,l),C_NE));
  769. end;
  770. begin
  771. helpsize:=12;
  772. dstref:=dest;
  773. srcref:=source;
  774. if cs_littlesize in aktglobalswitches then
  775. helpsize:=8;
  776. if not loadref and (len<=helpsize) then
  777. begin
  778. copysize:=4;
  779. cgsize:=OS_32;
  780. while len<>0 do
  781. begin
  782. if len<2 then
  783. begin
  784. copysize:=1;
  785. cgsize:=OS_8;
  786. end
  787. else if len<4 then
  788. begin
  789. copysize:=2;
  790. cgsize:=OS_16;
  791. end;
  792. dec(len,copysize);
  793. r:=rg.getregisterint(list,cgsize);
  794. a_load_ref_reg(list,cgsize,cgsize,srcref,r);
  795. if (len=0) and delsource then
  796. reference_release(list,source);
  797. a_load_reg_ref(list,cgsize,cgsize,r,dstref);
  798. inc(srcref.offset,copysize);
  799. inc(dstref.offset,copysize);
  800. rg.ungetregisterint(list,r);
  801. end;
  802. end
  803. else
  804. begin
  805. destreg:=rg.getregisterint(list,OS_ADDR);
  806. a_loadaddr_ref_reg(list,dest,destreg);
  807. srcreg:=rg.getregisterint(list,OS_ADDR);
  808. if loadref then
  809. a_load_ref_reg(list,OS_ADDR,OS_ADDR,source,srcreg)
  810. else
  811. begin
  812. a_loadaddr_ref_reg(list,source,srcreg);
  813. if delsource then
  814. begin
  815. srcref:=source;
  816. reference_release(list,srcref);
  817. end;
  818. end;
  819. countreg:=rg.getregisterint(list,OS_32);
  820. // if cs_littlesize in aktglobalswitches then
  821. genloop(len,1);
  822. {
  823. else
  824. begin
  825. helpsize:=len shr 2;
  826. len:=len and 3;
  827. if helpsize>1 then
  828. begin
  829. a_load_const_reg(list,OS_INT,helpsize,countreg);
  830. list.concat(Taicpu.op_none(A_REP,S_NO));
  831. end;
  832. if helpsize>0 then
  833. list.concat(Taicpu.op_none(A_MOVSD,S_NO));
  834. if len>1 then
  835. begin
  836. dec(len,2);
  837. list.concat(Taicpu.op_none(A_MOVSW,S_NO));
  838. end;
  839. if len=1 then
  840. list.concat(Taicpu.op_none(A_MOVSB,S_NO));
  841. end;
  842. }
  843. rg.ungetregisterint(list,countreg);
  844. rg.ungetregisterint(list,srcreg);
  845. rg.ungetregisterint(list,destreg);
  846. end;
  847. if delsource then
  848. tg.ungetiftemp(list,source);
  849. end;
  850. procedure tcgarm.g_overflowcheck(list: taasmoutput; const l: tlocation; def: tdef);
  851. begin
  852. end;
  853. procedure tcgarm.g_save_standard_registers(list : taasmoutput; usedinproc : Tsupregset);
  854. begin
  855. { we support only ARM standard calling conventions so this procedure has no use on the ARM }
  856. end;
  857. procedure tcgarm.g_restore_standard_registers(list : taasmoutput; usedinproc : Tsupregset);
  858. begin
  859. { we support only ARM standard calling conventions so this procedure has no use on the ARM }
  860. end;
  861. procedure tcgarm.g_save_all_registers(list : taasmoutput);
  862. begin
  863. { we support only ARM standard calling conventions so this procedure has no use on the ARM }
  864. end;
  865. procedure tcgarm.g_restore_all_registers(list : taasmoutput;accused,acchiused:boolean);
  866. begin
  867. { we support only ARM standard calling conventions so this procedure has no use on the ARM }
  868. end;
  869. procedure tcgarm.a_jmp_cond(list : taasmoutput;cond : TOpCmp;l: tasmlabel);
  870. var
  871. ai : taicpu;
  872. begin
  873. ai:=Taicpu.Op_sym(A_B,l);
  874. ai.SetCondition(OpCmp2AsmCond[cond]);
  875. ai.is_jmp:=true;
  876. list.concat(ai);
  877. end;
  878. procedure tcg64farm.a_op64_reg_reg(list : taasmoutput;op:TOpCG;regsrc,regdst : tregister64);
  879. var
  880. tmpreg : tregister;
  881. begin
  882. case op of
  883. OP_NEG:
  884. begin
  885. list.concat(setoppostfix(taicpu.op_reg_reg_const(A_RSB,regdst.reglo,regsrc.reglo,0),PF_S));
  886. list.concat(taicpu.op_reg_reg_const(A_RSC,regdst.reghi,regsrc.reghi,0));
  887. end;
  888. else
  889. a_op64_reg_reg_reg(list,op,regsrc,regdst,regdst);
  890. end;
  891. end;
  892. procedure tcg64farm.a_op64_const_reg(list : taasmoutput;op:TOpCG;value : qword;reg : tregister64);
  893. begin
  894. a_op64_const_reg_reg(list,op,value,reg,reg);
  895. end;
  896. procedure tcg64farm.a_op64_const_reg_reg(list: taasmoutput;op:TOpCG;value : qword;regsrc,regdst : tregister64);
  897. var
  898. tmpreg : tregister;
  899. b : byte;
  900. begin
  901. case op of
  902. OP_AND,OP_OR,OP_XOR:
  903. begin
  904. cg.a_op_const_reg_reg(list,op,OS_32,lo(value),regsrc.reglo,regdst.reglo);
  905. cg.a_op_const_reg_reg(list,op,OS_32,hi(value),regsrc.reghi,regdst.reghi);
  906. end;
  907. OP_ADD:
  908. begin
  909. if is_shifter_const(lo(value),b) then
  910. list.concat(setoppostfix(taicpu.op_reg_reg_const(A_ADD,regdst.reglo,regsrc.reglo,lo(value)),PF_S))
  911. else
  912. begin
  913. tmpreg:=rg.getregisterint(list,OS_32);
  914. cg.a_load_const_reg(list,OS_32,lo(value),tmpreg);
  915. list.concat(setoppostfix(taicpu.op_reg_reg_reg(A_ADD,regdst.reglo,regsrc.reglo,tmpreg),PF_S));
  916. rg.ungetregisterint(list,tmpreg);
  917. end;
  918. if is_shifter_const(hi(value),b) then
  919. list.concat(taicpu.op_reg_reg_const(A_ADC,regdst.reghi,regsrc.reghi,hi(value)))
  920. else
  921. begin
  922. tmpreg:=rg.getregisterint(list,OS_32);
  923. cg.a_load_const_reg(list,OS_32,hi(value),tmpreg);
  924. list.concat(taicpu.op_reg_reg_reg(A_ADC,regdst.reghi,regsrc.reghi,tmpreg));
  925. rg.ungetregisterint(list,tmpreg);
  926. end;
  927. end;
  928. OP_SUB:
  929. begin
  930. if is_shifter_const(lo(value),b) then
  931. list.concat(setoppostfix(taicpu.op_reg_reg_const(A_SUB,regdst.reglo,regsrc.reglo,lo(value)),PF_S))
  932. else
  933. begin
  934. tmpreg:=rg.getregisterint(list,OS_32);
  935. cg.a_load_const_reg(list,OS_32,lo(value),tmpreg);
  936. list.concat(setoppostfix(taicpu.op_reg_reg_reg(A_SUB,regdst.reglo,regsrc.reglo,tmpreg),PF_S));
  937. rg.ungetregisterint(list,tmpreg);
  938. end;
  939. if is_shifter_const(hi(value),b) then
  940. list.concat(taicpu.op_reg_reg_const(A_SBC,regdst.reghi,regsrc.reghi,hi(value)))
  941. else
  942. begin
  943. tmpreg:=rg.getregisterint(list,OS_32);
  944. cg.a_load_const_reg(list,OS_32,hi(value),tmpreg);
  945. list.concat(taicpu.op_reg_reg_reg(A_SBC,regdst.reghi,regsrc.reghi,tmpreg));
  946. rg.ungetregisterint(list,tmpreg);
  947. end;
  948. end;
  949. else
  950. internalerror(2003083101);
  951. end;
  952. end;
  953. procedure tcg64farm.a_op64_reg_reg_reg(list: taasmoutput;op:TOpCG;regsrc1,regsrc2,regdst : tregister64);
  954. begin
  955. case op of
  956. OP_AND,OP_OR,OP_XOR:
  957. begin
  958. cg.a_op_reg_reg_reg(list,op,OS_32,regsrc1.reglo,regsrc2.reglo,regdst.reglo);
  959. cg.a_op_reg_reg_reg(list,op,OS_32,regsrc1.reghi,regsrc2.reghi,regdst.reghi);
  960. end;
  961. OP_ADD:
  962. begin
  963. list.concat(setoppostfix(taicpu.op_reg_reg_reg(A_ADD,regdst.reglo,regsrc1.reglo,regsrc2.reglo),PF_S));
  964. list.concat(taicpu.op_reg_reg_reg(A_ADC,regdst.reghi,regsrc1.reghi,regsrc2.reghi));
  965. end;
  966. OP_SUB:
  967. begin
  968. list.concat(setoppostfix(taicpu.op_reg_reg_reg(A_SUB,regdst.reglo,regsrc2.reglo,regsrc1.reglo),PF_S));
  969. list.concat(taicpu.op_reg_reg_reg(A_SBC,regdst.reghi,regsrc2.reghi,regsrc1.reghi));
  970. end;
  971. else
  972. internalerror(2003083101);
  973. end;
  974. end;
  975. begin
  976. cg:=tcgarm.create;
  977. cg64:=tcg64farm.create;
  978. end.
  979. {
  980. $Log$
  981. Revision 1.12 2003-09-03 19:10:30 florian
  982. * initial revision of new register naming
  983. Revision 1.11 2003/09/03 11:18:37 florian
  984. * fixed arm concatcopy
  985. + arm support in the common compiler sources added
  986. * moved some generic cg code around
  987. + tfputype added
  988. * ...
  989. Revision 1.10 2003/09/01 15:11:16 florian
  990. * fixed reference handling
  991. * fixed operand postfix for floating point instructions
  992. * fixed wrong shifter constant handling
  993. Revision 1.9 2003/09/01 09:54:57 florian
  994. * results of work on arm port last weekend
  995. Revision 1.8 2003/08/29 21:36:28 florian
  996. * fixed procedure entry/exit code
  997. * started to fix reference handling
  998. Revision 1.7 2003/08/28 13:26:10 florian
  999. * another couple of arm fixes
  1000. Revision 1.6 2003/08/28 00:05:29 florian
  1001. * today's arm patches
  1002. Revision 1.5 2003/08/25 23:20:38 florian
  1003. + started to implement FPU support for the ARM
  1004. * fixed a lot of other things
  1005. Revision 1.4 2003/08/24 12:27:26 florian
  1006. * continued to work on the arm port
  1007. Revision 1.3 2003/08/21 03:14:00 florian
  1008. * arm compiler can be compiled; far from being working
  1009. Revision 1.2 2003/08/20 15:50:12 florian
  1010. * more arm stuff
  1011. Revision 1.1 2003/07/21 16:35:30 florian
  1012. * very basic stuff for the arm
  1013. }