agjasmin.pas 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. {
  2. Copyright (c) 1998-2010 by the Free Pascal team
  3. This unit implements the Jasmin assembler writer
  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 for writing Jasmin assembler (JVM bytecode) output.
  18. }
  19. unit agjasmin;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. cclasses,systems,
  24. globtype,globals,
  25. symconst,symbase,symdef,symsym,
  26. aasmbase,aasmtai,aasmdata,aasmcpu,
  27. assemble;
  28. type
  29. TJasminAssemblerOutputFile=class(TExternalAssemblerOutputFile)
  30. procedure RemoveAsm; override;
  31. end;
  32. TJasminInstrWriter = class;
  33. {# This is a derived class which is used to write
  34. Jasmin-styled assembler.
  35. }
  36. { TJasminAssembler }
  37. TJasminAssembler=class(texternalassembler)
  38. protected
  39. jasminjar: tcmdstr;
  40. asmfiles: TCmdStrList;
  41. procedure WriteExtraHeader(obj: tabstractrecorddef);
  42. procedure WriteInstruction(hp: tai);
  43. procedure NewAsmFileForStructDef(obj: tabstractrecorddef);
  44. function VisibilityToStr(vis: tvisibility): ansistring;
  45. function MethodDefinition(pd: tprocdef): ansistring;
  46. function ConstValue(csym: tconstsym): ansistring;
  47. function ConstAssignmentValue(csym: tconstsym): ansistring;
  48. function ConstDefinition(sym: tconstsym): ansistring;
  49. function FieldDefinition(sym: tabstractvarsym): ansistring;
  50. function InnerStructDef(obj: tabstractrecorddef): ansistring;
  51. procedure WriteProcDef(pd: tprocdef);
  52. procedure WriteFieldSym(sym: tabstractvarsym);
  53. procedure WriteConstSym(sym: tconstsym);
  54. procedure WriteSymtableVarSyms(st: TSymtable);
  55. procedure WriteSymtableProcdefs(st: TSymtable);
  56. procedure WriteSymtableStructDefs(st: TSymtable);
  57. public
  58. constructor Create(info: pasminfo; smart: boolean); override;
  59. function MakeCmdLine: TCmdStr;override;
  60. procedure WriteTree(p:TAsmList);override;
  61. procedure WriteAsmList;override;
  62. destructor destroy; override;
  63. protected
  64. InstrWriter: TJasminInstrWriter;
  65. end;
  66. {# This is the base class for writing instructions.
  67. The WriteInstruction() method must be overridden
  68. to write a single instruction to the assembler
  69. file.
  70. }
  71. { TJasminInstrWriter }
  72. TJasminInstrWriter = class
  73. constructor create(_owner: TJasminAssembler);
  74. procedure WriteInstruction(hp : tai); virtual;
  75. protected
  76. owner: TJasminAssembler;
  77. end;
  78. implementation
  79. uses
  80. SysUtils,
  81. cutils,cfileutl,script,
  82. fmodule,finput,verbose,
  83. symtype,symcpu,symtable,jvmdef,
  84. itcpujas,cpubase,cpuinfo,cgutils,
  85. widestr
  86. ;
  87. const
  88. line_length = 70;
  89. type
  90. t64bitarray = array[0..7] of byte;
  91. t32bitarray = array[0..3] of byte;
  92. {****************************************************************************}
  93. { Support routines }
  94. {****************************************************************************}
  95. function fixline(s:string):string;
  96. {
  97. return s with all leading and ending spaces and tabs removed
  98. }
  99. var
  100. i,j,k : integer;
  101. begin
  102. i:=length(s);
  103. while (i>0) and (s[i] in [#9,' ']) do
  104. dec(i);
  105. j:=1;
  106. while (j<i) and (s[j] in [#9,' ']) do
  107. inc(j);
  108. for k:=j to i do
  109. if s[k] in [#0..#31,#127..#255] then
  110. s[k]:='.';
  111. fixline:=Copy(s,j,i-j+1);
  112. end;
  113. function constastr(p: pchar; len: longint): ansistring;
  114. var
  115. i,runstart,runlen: longint;
  116. procedure flush;
  117. begin
  118. if runlen>0 then
  119. begin
  120. setlength(result,length(result)+runlen);
  121. move(p[runstart],result[length(result)-runlen+1],runlen);
  122. runlen:=0;
  123. end;
  124. end;
  125. begin
  126. result:='"';
  127. runlen:=0;
  128. runstart:=0;
  129. for i:=0 to len-1 do
  130. begin
  131. { escape control codes }
  132. case p[i] of
  133. { LF and CR must be escaped specially, because \uXXXX parsing
  134. happens in the pre-processor, so it's the same as actually
  135. inserting a newline in the middle of a string constant }
  136. #10:
  137. begin
  138. flush;
  139. result:=result+'\n';
  140. end;
  141. #13:
  142. begin
  143. flush;
  144. result:=result+'\r';
  145. end;
  146. '"','\':
  147. begin
  148. flush;
  149. result:=result+'\'+p[i];
  150. end
  151. else if p[i]<#32 then
  152. begin
  153. flush;
  154. result:=result+'\u'+hexstr(ord(p[i]),4);
  155. end
  156. else if p[i]<#127 then
  157. begin
  158. if runlen=0 then
  159. runstart:=i;
  160. inc(runlen);
  161. end
  162. else
  163. begin
  164. { see comments in njvmcon }
  165. flush;
  166. result:=result+'\u'+hexstr(ord(p[i]),4)
  167. end;
  168. end;
  169. end;
  170. flush;
  171. result:=result+'"';
  172. end;
  173. function constwstr(w: pcompilerwidechar; len: longint): ansistring;
  174. var
  175. i: longint;
  176. begin
  177. result:='"';
  178. for i:=0 to len-1 do
  179. begin
  180. { escape control codes }
  181. case w[i] of
  182. 10:
  183. result:=result+'\n';
  184. 13:
  185. result:=result+'\r';
  186. ord('"'),ord('\'):
  187. result:=result+'\'+chr(w[i]);
  188. else if (w[i]<32) or
  189. (w[i]>=127) then
  190. result:=result+'\u'+hexstr(w[i],4)
  191. else
  192. result:=result+char(w[i]);
  193. end;
  194. end;
  195. result:=result+'"';
  196. end;
  197. function constsingle(s: single): ansistring;
  198. begin
  199. result:='0fx'+hexstr(longint(t32bitarray(s)),8);
  200. end;
  201. function constdouble(d: double): ansistring;
  202. begin
  203. // force interpretation as double (since we write it out as an
  204. // integer, we never have to swap the endianess). We have to
  205. // include the sign separately because of the way Java parses
  206. // hex numbers (0x8000000000000000 is not a valid long)
  207. result:=hexstr(abs(int64(t64bitarray(d))),16);
  208. if int64(t64bitarray(d))<0 then
  209. result:='-'+result;
  210. result:='0dx'+result;
  211. end;
  212. {****************************************************************************}
  213. { Jasmin Output File }
  214. {****************************************************************************}
  215. procedure TJasminAssemblerOutputFile.RemoveAsm;
  216. var
  217. g : file;
  218. begin
  219. inherited;
  220. if cs_asm_leave in current_settings.globalswitches then
  221. exit;
  222. while not TJasminAssembler(owner).asmfiles.empty do
  223. begin
  224. if cs_asm_extern in current_settings.globalswitches then
  225. AsmRes.AddDeleteCommand(TJasminAssembler(owner).asmfiles.GetFirst)
  226. else
  227. begin
  228. assign(g,TJasminAssembler(owner).asmfiles.GetFirst);
  229. {$I-}
  230. erase(g);
  231. {$I+}
  232. if ioresult<>0 then;
  233. end;
  234. end;
  235. end;
  236. {****************************************************************************}
  237. { Jasmin Assembler writer }
  238. {****************************************************************************}
  239. destructor TJasminAssembler.Destroy;
  240. begin
  241. InstrWriter.free;
  242. asmfiles.free;
  243. inherited destroy;
  244. end;
  245. procedure TJasminAssembler.WriteTree(p:TAsmList);
  246. var
  247. ch : char;
  248. hp : tai;
  249. hp1 : tailineinfo;
  250. s : ansistring;
  251. i,pos : longint;
  252. InlineLevel : longint;
  253. do_line : boolean;
  254. begin
  255. if not assigned(p) then
  256. exit;
  257. InlineLevel:=0;
  258. { lineinfo is only needed for al_procedures (PFV) }
  259. do_line:=(cs_asm_source in current_settings.globalswitches);
  260. hp:=tai(p.first);
  261. while assigned(hp) do
  262. begin
  263. prefetch(pointer(hp.next)^);
  264. if not(hp.typ in SkipLineInfo) then
  265. begin
  266. hp1 := hp as tailineinfo;
  267. current_filepos:=hp1.fileinfo;
  268. { no line info for inlined code }
  269. if do_line and (inlinelevel=0) then
  270. begin
  271. { load infile }
  272. if lastfileinfo.fileindex<>hp1.fileinfo.fileindex then
  273. begin
  274. infile:=current_module.sourcefiles.get_file(hp1.fileinfo.fileindex);
  275. if assigned(infile) then
  276. begin
  277. { open only if needed !! }
  278. if (cs_asm_source in current_settings.globalswitches) then
  279. infile.open;
  280. end;
  281. { avoid unnecessary reopens of the same file !! }
  282. lastfileinfo.fileindex:=hp1.fileinfo.fileindex;
  283. { be sure to change line !! }
  284. lastfileinfo.line:=-1;
  285. end;
  286. { write source }
  287. if (cs_asm_source in current_settings.globalswitches) and
  288. assigned(infile) then
  289. begin
  290. if (infile<>lastinfile) then
  291. begin
  292. writer.AsmWriteLn(asminfo^.comment+'['+infile.name+']');
  293. if assigned(lastinfile) then
  294. lastinfile.close;
  295. end;
  296. if (hp1.fileinfo.line<>lastfileinfo.line) and
  297. ((hp1.fileinfo.line<infile.maxlinebuf) or (InlineLevel>0)) then
  298. begin
  299. if (hp1.fileinfo.line<>0) and
  300. ((infile.linebuf^[hp1.fileinfo.line]>=0) or (InlineLevel>0)) then
  301. writer.AsmWriteLn(asminfo^.comment+'['+tostr(hp1.fileinfo.line)+'] '+
  302. fixline(infile.GetLineStr(hp1.fileinfo.line)));
  303. { set it to a negative value !
  304. to make that is has been read already !! PM }
  305. if (infile.linebuf^[hp1.fileinfo.line]>=0) then
  306. infile.linebuf^[hp1.fileinfo.line]:=-infile.linebuf^[hp1.fileinfo.line]-1;
  307. end;
  308. end;
  309. lastfileinfo:=hp1.fileinfo;
  310. lastinfile:=infile;
  311. end;
  312. end;
  313. case hp.typ of
  314. ait_comment :
  315. Begin
  316. writer.AsmWrite(asminfo^.comment);
  317. writer.AsmWritePChar(tai_comment(hp).str);
  318. writer.AsmLn;
  319. End;
  320. ait_regalloc :
  321. begin
  322. if (cs_asm_regalloc in current_settings.globalswitches) then
  323. begin
  324. writer.AsmWrite(#9+asminfo^.comment+'Register ');
  325. repeat
  326. writer.AsmWrite(std_regname(Tai_regalloc(hp).reg));
  327. if (hp.next=nil) or
  328. (tai(hp.next).typ<>ait_regalloc) or
  329. (tai_regalloc(hp.next).ratype<>tai_regalloc(hp).ratype) then
  330. break;
  331. hp:=tai(hp.next);
  332. writer.AsmWrite(',');
  333. until false;
  334. writer.AsmWrite(' ');
  335. writer.AsmWriteLn(regallocstr[tai_regalloc(hp).ratype]);
  336. end;
  337. end;
  338. ait_tempalloc :
  339. begin
  340. if (cs_asm_tempalloc in current_settings.globalswitches) then
  341. begin
  342. {$ifdef EXTDEBUG}
  343. if assigned(tai_tempalloc(hp).problem) then
  344. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  345. tostr(tai_tempalloc(hp).tempsize)+' '+tai_tempalloc(hp).problem^)
  346. else
  347. {$endif EXTDEBUG}
  348. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  349. tostr(tai_tempalloc(hp).tempsize)+' '+tempallocstr[tai_tempalloc(hp).allocation]);
  350. end;
  351. end;
  352. ait_align :
  353. begin
  354. end;
  355. ait_section :
  356. begin
  357. end;
  358. ait_datablock :
  359. begin
  360. internalerror(2010122701);
  361. end;
  362. ait_const:
  363. begin
  364. writer.AsmWriteln('constant');
  365. // internalerror(2010122702);
  366. end;
  367. ait_realconst :
  368. begin
  369. internalerror(2010122703);
  370. end;
  371. ait_string :
  372. begin
  373. pos:=0;
  374. for i:=1 to tai_string(hp).len do
  375. begin
  376. if pos=0 then
  377. begin
  378. writer.AsmWrite(#9'strconst: '#9'"');
  379. pos:=20;
  380. end;
  381. ch:=tai_string(hp).str[i-1];
  382. case ch of
  383. #0, {This can't be done by range, because a bug in FPC}
  384. #1..#31,
  385. #128..#255 : s:='\'+tostr(ord(ch) shr 6)+tostr((ord(ch) and 63) shr 3)+tostr(ord(ch) and 7);
  386. '"' : s:='\"';
  387. '\' : s:='\\';
  388. else
  389. s:=ch;
  390. end;
  391. writer.AsmWrite(s);
  392. inc(pos,length(s));
  393. if (pos>line_length) or (i=tai_string(hp).len) then
  394. begin
  395. writer.AsmWriteLn('"');
  396. pos:=0;
  397. end;
  398. end;
  399. end;
  400. ait_label :
  401. begin
  402. if (tai_label(hp).labsym.is_used) then
  403. begin
  404. writer.AsmWrite(tai_label(hp).labsym.name);
  405. writer.AsmWriteLn(':');
  406. end;
  407. end;
  408. ait_symbol :
  409. begin
  410. if (tai_symbol(hp).sym.typ = AT_FUNCTION) then
  411. begin
  412. end
  413. else
  414. begin
  415. writer.AsmWrite('data symbol: ');
  416. writer.AsmWriteln(tai_symbol(hp).sym.name);
  417. // internalerror(2010122706);
  418. end;
  419. end;
  420. ait_symbol_end :
  421. begin
  422. end;
  423. ait_instruction :
  424. begin
  425. WriteInstruction(hp);
  426. end;
  427. ait_force_line,
  428. ait_function_name : ;
  429. ait_cutobject :
  430. begin
  431. end;
  432. ait_marker :
  433. if tai_marker(hp).kind=mark_NoLineInfoStart then
  434. inc(InlineLevel)
  435. else if tai_marker(hp).kind=mark_NoLineInfoEnd then
  436. dec(InlineLevel);
  437. ait_directive :
  438. begin
  439. writer.AsmWrite('.'+directivestr[tai_directive(hp).directive]+' ');
  440. if tai_directive(hp).name<>'' then
  441. writer.AsmWrite(tai_directive(hp).name);
  442. writer.AsmLn;
  443. end;
  444. ait_jvar:
  445. begin
  446. writer.AsmWrite('.var ');
  447. writer.AsmWrite(tostr(tai_jvar(hp).stackslot));
  448. writer.AsmWrite(' is ');
  449. writer.AsmWrite(tai_jvar(hp).desc^);
  450. writer.AsmWrite(' from ');
  451. writer.AsmWrite(tai_jvar(hp).startlab.name);
  452. writer.AsmWrite(' to ');
  453. writer.AsmWriteLn(tai_jvar(hp).stoplab.name);
  454. end;
  455. ait_jcatch:
  456. begin
  457. writer.AsmWrite('.catch ');
  458. writer.AsmWrite(tai_jcatch(hp).name^);
  459. writer.AsmWrite(' from ');
  460. writer.AsmWrite(tai_jcatch(hp).startlab.name);
  461. writer.AsmWrite(' to ');
  462. writer.AsmWrite(tai_jcatch(hp).stoplab.name);
  463. writer.AsmWrite(' using ');
  464. writer.AsmWriteLn(tai_jcatch(hp).handlerlab.name);
  465. end;
  466. else
  467. internalerror(2010122707);
  468. end;
  469. hp:=tai(hp.next);
  470. end;
  471. end;
  472. procedure TJasminAssembler.WriteExtraHeader(obj: tabstractrecorddef);
  473. var
  474. superclass,
  475. intf: tobjectdef;
  476. n: ansistring;
  477. i: longint;
  478. toplevelowner: tsymtable;
  479. begin
  480. superclass:=nil;
  481. { JVM 1.5+ }
  482. writer.AsmWriteLn('.bytecode 49.0');
  483. // include files are not support by Java, and the directory of the main
  484. // source file must not be specified
  485. if current_module.mainsource<>'' then
  486. n:=ExtractFileName(current_module.mainsource)
  487. else
  488. n:=InputFileName;
  489. writer.AsmWriteLn('.source '+ExtractFileName(n));
  490. { class/interface name }
  491. if not assigned(obj) then
  492. begin
  493. { fake class type for unit -> name=unitname and
  494. superclass=java.lang.object, make final so you cannot descend
  495. from it }
  496. writer.AsmWrite('.class final public ');
  497. if assigned(current_module.namespace) then
  498. writer.AsmWrite(current_module.namespace^+'.');
  499. writer.AsmWriteln(current_module.realmodulename^);
  500. writer.AsmWriteLn('.super java/lang/Object');
  501. end
  502. else
  503. begin
  504. toplevelowner:=obj.owner;
  505. while not(toplevelowner.symtabletype in [staticsymtable,globalsymtable]) do
  506. toplevelowner:=toplevelowner.defowner.owner;
  507. case obj.typ of
  508. recorddef:
  509. begin
  510. { can't inherit from records }
  511. writer.AsmWrite('.class final ');
  512. if toplevelowner.symtabletype=globalsymtable then
  513. writer.AsmWrite('public ');
  514. writer.AsmWriteln(obj.jvm_full_typename(true));
  515. superclass:=java_fpcbaserecordtype;
  516. end;
  517. objectdef:
  518. begin
  519. case tobjectdef(obj).objecttype of
  520. odt_javaclass:
  521. begin
  522. writer.AsmWrite('.class ');
  523. if oo_is_sealed in tobjectdef(obj).objectoptions then
  524. writer.AsmWrite('final ');
  525. if (oo_is_abstract in tobjectdef(obj).objectoptions) or
  526. (tobjectdef(obj).abstractcnt<>0) then
  527. writer.AsmWrite('abstract ');
  528. if toplevelowner.symtabletype=globalsymtable then
  529. writer.AsmWrite('public ');
  530. if (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  531. writer.AsmWrite('enum ');
  532. writer.AsmWriteln(obj.jvm_full_typename(true));
  533. superclass:=tobjectdef(obj).childof;
  534. end;
  535. odt_interfacejava:
  536. begin
  537. writer.AsmWrite('.interface abstract ');
  538. if toplevelowner.symtabletype=globalsymtable then
  539. writer.AsmWrite('public ');
  540. writer.AsmWriteLn(obj.jvm_full_typename(true));
  541. { interfaces must always specify Java.lang.object as
  542. superclass }
  543. superclass:=java_jlobject;
  544. end
  545. else
  546. internalerror(2011010906);
  547. end;
  548. end;
  549. end;
  550. { superclass }
  551. if assigned(superclass) then
  552. begin
  553. writer.AsmWrite('.super ');
  554. if assigned(superclass.import_lib) then
  555. writer.AsmWrite(superclass.import_lib^+'/');
  556. writer.AsmWriteln(superclass.objextname^);
  557. end;
  558. { implemented interfaces }
  559. if (obj.typ=objectdef) and
  560. assigned(tobjectdef(obj).ImplementedInterfaces) then
  561. begin
  562. for i:=0 to tobjectdef(obj).ImplementedInterfaces.count-1 do
  563. begin
  564. intf:=TImplementedInterface(tobjectdef(obj).ImplementedInterfaces[i]).IntfDef;
  565. writer.AsmWrite('.implements ');
  566. writer.AsmWriteLn(intf.jvm_full_typename(true));
  567. end;
  568. end;
  569. { signature for enum classes (must come after superclass and
  570. implemented interfaces) }
  571. if (obj.typ=objectdef) and
  572. (oo_is_enum_class in tobjectdef(obj).objectoptions) then
  573. writer.AsmWriteln('.signature "Ljava/lang/Enum<L'+obj.jvm_full_typename(true)+';>;"');
  574. { in case of nested class: relation to parent class }
  575. if obj.owner.symtabletype in [objectsymtable,recordsymtable] then
  576. writer.AsmWriteln(InnerStructDef(obj));
  577. { add all nested classes }
  578. for i:=0 to obj.symtable.deflist.count-1 do
  579. if (is_java_class_or_interface(tdef(obj.symtable.deflist[i])) or
  580. (tdef(obj.symtable.deflist[i]).typ=recorddef)) and
  581. not(df_generic in tdef(obj.symtable.deflist[i]).defoptions) then
  582. writer.AsmWriteln(InnerStructDef(tabstractrecorddef(obj.symtable.deflist[i])));
  583. end;
  584. writer.AsmLn;
  585. end;
  586. procedure TJasminAssembler.WriteInstruction(hp: tai);
  587. begin
  588. InstrWriter.WriteInstruction(hp);
  589. end;
  590. function TJasminAssembler.MakeCmdLine: TCmdStr;
  591. const
  592. jasminjarname = 'jasmin.jar';
  593. var
  594. filenames: tcmdstr;
  595. asmfile: tcmdstrlistitem;
  596. jasminjarfound: boolean;
  597. begin
  598. if jasminjar='' then
  599. begin
  600. jasminjarfound:=false;
  601. if utilsdirectory<>'' then
  602. jasminjarfound:=FindFile(jasminjarname,utilsdirectory,false,jasminjar);
  603. if not jasminjarfound then
  604. jasminjarfound:=FindFileInExeLocations(jasminjarname,false,jasminjar);
  605. if (not jasminjarfound) and not(cs_asm_extern in current_settings.globalswitches) then
  606. begin
  607. Message1(exec_e_assembler_not_found,jasminjarname);
  608. current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
  609. end;
  610. if jasminjarfound then
  611. Message1(exec_t_using_assembler,jasminjar);
  612. end;
  613. result:=asminfo^.asmcmd;
  614. filenames:=ScriptFixFileName(AsmFileName);
  615. if cs_asm_extern in current_settings.globalswitches then
  616. filenames:=maybequoted(filenames);
  617. asmfile:=tcmdstrlistitem(asmfiles.First);
  618. while assigned(asmfile) do
  619. begin
  620. if cs_asm_extern in current_settings.globalswitches then
  621. filenames:=filenames+' '+maybequoted(ScriptFixFileName(asmfile.str))
  622. else
  623. filenames:=filenames+' '+ScriptFixFileName(asmfile.str);
  624. asmfile:=tcmdstrlistitem(asmfile.next);
  625. end;
  626. Replace(result,'$ASM',filenames);
  627. if (path<>'') then
  628. if cs_asm_extern in current_settings.globalswitches then
  629. Replace(result,'$OBJDIR',maybequoted(ScriptFixFileName(path)))
  630. else
  631. Replace(result,'$OBJDIR',ScriptFixFileName(path))
  632. else
  633. Replace(result,'$OBJDIR','.');
  634. if cs_asm_extern in current_settings.globalswitches then
  635. Replace(result,'$JASMINJAR',maybequoted(ScriptFixFileName(jasminjar)))
  636. else
  637. Replace(result,'$JASMINJAR',ScriptFixFileName(jasminjar));
  638. Replace(result,'$EXTRAOPT',asmextraopt);
  639. end;
  640. procedure TJasminAssembler.NewAsmFileForStructDef(obj: tabstractrecorddef);
  641. begin
  642. if not writer.ClearIfEmpty then
  643. begin
  644. writer.AsmClose;
  645. asmfiles.Concat(AsmFileName);
  646. end;
  647. AsmFileName:=obj.jvm_full_typename(false);
  648. AsmFileName:=Path+FixFileName(AsmFileName)+target_info.asmext;
  649. writer.AsmCreate(cut_normal);
  650. end;
  651. function TJasminAssembler.VisibilityToStr(vis: tvisibility): ansistring;
  652. begin
  653. case vis of
  654. vis_hidden,
  655. vis_strictprivate:
  656. result:='private ';
  657. { protected in Java means "accessible by subclasses *and* by classes
  658. in the same package" -> similar to regular "protected" in Pascal;
  659. "strict protected" is actually more strict in Pascal than in Java,
  660. but there's not much we can do about that }
  661. vis_protected,
  662. vis_strictprotected:
  663. result:='protected ';
  664. vis_private:
  665. { pick default visibility = "package" visibility; required because
  666. other classes in the same unit can also access these symbols }
  667. result:='';
  668. vis_public:
  669. result:='public '
  670. else
  671. internalerror(2010122609);
  672. end;
  673. end;
  674. function TJasminAssembler.MethodDefinition(pd: tprocdef): ansistring;
  675. begin
  676. result:=VisibilityToStr(pd.visibility);
  677. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  678. (po_classmethod in pd.procoptions) then
  679. result:=result+'static ';
  680. if (po_abstractmethod in pd.procoptions) or
  681. is_javainterface(tdef(pd.owner.defowner)) then
  682. result:=result+'abstract ';
  683. if (pd.procsym.owner.symtabletype in [globalsymtable,staticsymtable,localsymtable]) or
  684. (po_finalmethod in pd.procoptions) or
  685. (not(po_virtualmethod in pd.procoptions) and
  686. not(po_classmethod in pd.procoptions) and
  687. not(pd.proctypeoption in [potype_constructor,potype_class_constructor])) then
  688. result:=result+'final ';
  689. result:=result+tcpuprocdef(pd).jvmmangledbasename(false);
  690. end;
  691. function TJasminAssembler.ConstValue(csym: tconstsym): ansistring;
  692. begin
  693. case csym.consttyp of
  694. constord:
  695. { always interpret as signed value, because the JVM does not
  696. support unsigned values }
  697. case csym.constdef.size of
  698. 1:result:=tostr(shortint(csym.value.valueord.svalue));
  699. 2:result:=tostr(smallint(csym.value.valueord.svalue));
  700. 4:result:=tostr(longint(csym.value.valueord.svalue));
  701. 8:result:=tostr(csym.value.valueord.svalue);
  702. else
  703. internalerror(2014082050);
  704. end;
  705. conststring:
  706. result:=constastr(pchar(csym.value.valueptr),csym.value.len);
  707. constreal:
  708. case tfloatdef(csym.constdef).floattype of
  709. s32real:
  710. result:=constsingle(pbestreal(csym.value.valueptr)^);
  711. s64real:
  712. result:=constdouble(pbestreal(csym.value.valueptr)^);
  713. else
  714. internalerror(2011021204);
  715. end;
  716. constset:
  717. result:='TODO: add support for constant sets';
  718. constpointer:
  719. { can only be null, but that's the default value and should not
  720. be written; there's no primitive type that can hold nill }
  721. internalerror(2011021201);
  722. constnil:
  723. internalerror(2011021202);
  724. constresourcestring:
  725. result:='TODO: add support for constant resource strings';
  726. constwstring:
  727. result:=constwstr(pcompilerwidestring(csym.value.valueptr)^.data,pcompilerwidestring(csym.value.valueptr)^.len);
  728. constguid:
  729. result:='TODO: add support for constant guids';
  730. else
  731. internalerror(2011021205);
  732. end;
  733. end;
  734. function TJasminAssembler.ConstAssignmentValue(csym: tconstsym): ansistring;
  735. begin
  736. result:='';
  737. { nil is the default value -> don't write explicitly }
  738. case csym.consttyp of
  739. constpointer:
  740. begin
  741. if csym.value.valueordptr<>0 then
  742. internalerror(2011021206);
  743. end;
  744. constnil:
  745. ;
  746. else
  747. begin
  748. { enums and sets are initialized as typed constants }
  749. if not assigned(csym.constdef) or
  750. not(csym.constdef.typ in [enumdef,setdef]) then
  751. result:=' = '+ConstValue(csym);
  752. end;
  753. end;
  754. end;
  755. function TJasminAssembler.ConstDefinition(sym: tconstsym): ansistring;
  756. begin
  757. result:=VisibilityToStr(sym.visibility);
  758. { formal constants are always class-level, not instance-level }
  759. result:=result+'static final ';
  760. if sp_internal in sym.symoptions then
  761. result:=result+'synthetic ';
  762. result:=result+jvmmangledbasename(sym,true);
  763. result:=result+ConstAssignmentValue(tconstsym(sym));
  764. end;
  765. function TJasminAssembler.FieldDefinition(sym: tabstractvarsym): ansistring;
  766. begin
  767. case sym.typ of
  768. staticvarsym:
  769. begin
  770. if sym.owner.symtabletype=globalsymtable then
  771. result:='public '
  772. else
  773. { package visbility }
  774. result:='';
  775. end;
  776. fieldvarsym,
  777. absolutevarsym:
  778. result:=VisibilityToStr(tstoredsym(sym).visibility);
  779. else
  780. internalerror(2011011204);
  781. end;
  782. if (sym.typ=staticvarsym) or
  783. (sp_static in sym.symoptions) then
  784. result:=result+'static ';
  785. if sym.varspez in [vs_const,vs_final] then
  786. result:=result+'final ';
  787. if sp_internal in sym.symoptions then
  788. result:=result+'synthetic ';
  789. { mark the class fields of enum classes that contain the initialised
  790. enum instances as "enum" (recognise them by the fact that their type
  791. is the same as their parent class, and that this parent class is
  792. marked as oo_is_enum_class) }
  793. if assigned(sym.owner.defowner) and
  794. (tdef(sym.owner.defowner).typ=objectdef) and
  795. (oo_is_enum_class in tobjectdef(sym.owner.defowner).objectoptions) and
  796. (sym.typ=staticvarsym) and
  797. (tstaticvarsym(sym).vardef=tdef(sym.owner.defowner)) then
  798. result:=result+'enum ';
  799. result:=result+jvmmangledbasename(sym,true);
  800. end;
  801. function TJasminAssembler.InnerStructDef(obj: tabstractrecorddef): ansistring;
  802. var
  803. extname: pshortstring;
  804. kindname: ansistring;
  805. begin
  806. if not(obj.owner.defowner.typ in [objectdef,recorddef]) then
  807. internalerror(2011021701);
  808. { Nested classes in the Pascal sense are equivalent to "static"
  809. inner classes in Java -- will be changed when support for
  810. Java-style non-static classes is added }
  811. case obj.typ of
  812. recorddef:
  813. begin
  814. kindname:='class static ';
  815. extname:=obj.symtable.realname;
  816. end;
  817. objectdef:
  818. begin
  819. extname:=tobjectdef(obj).objextname;
  820. case tobjectdef(obj).objecttype of
  821. odt_javaclass:
  822. kindname:='class static ';
  823. odt_interfacejava:
  824. kindname:='interface static abstract ';
  825. else
  826. internalerror(2011021702);
  827. end;
  828. end;
  829. else
  830. internalerror(2011032809);
  831. end;
  832. result:=
  833. '.inner '+
  834. kindname+
  835. VisibilityToStr(obj.typesym.visibility)+
  836. extname^+
  837. ' inner '+
  838. obj.jvm_full_typename(true)+
  839. ' outer '+
  840. tabstractrecorddef(obj.owner.defowner).jvm_full_typename(true);
  841. end;
  842. procedure TJasminAssembler.WriteProcDef(pd: tprocdef);
  843. begin
  844. if not assigned(tcpuprocdef(pd).exprasmlist) and
  845. not(po_abstractmethod in pd.procoptions) and
  846. (not is_javainterface(pd.struct) or
  847. (pd.proctypeoption in [potype_unitinit,potype_unitfinalize])) then
  848. exit;
  849. writer.AsmWrite('.method ');
  850. writer.AsmWriteln(MethodDefinition(pd));
  851. if jvmtypeneedssignature(pd) then
  852. begin
  853. writer.AsmWrite('.signature "');
  854. writer.AsmWrite(tcpuprocdef(pd).jvmmangledbasename(true));
  855. writer.AsmWriteln('"');
  856. end;
  857. WriteTree(tcpuprocdef(pd).exprasmlist);
  858. writer.AsmWriteln('.end method');
  859. writer.AsmLn;
  860. end;
  861. procedure TJasminAssembler.WriteFieldSym(sym: tabstractvarsym);
  862. begin
  863. { internal static field definition alias -> skip }
  864. if (sym.owner.symtabletype in [recordsymtable,ObjectSymtable]) and
  865. (sym.typ=staticvarsym) then
  866. exit;
  867. { external or threadvar definition -> no definition here }
  868. if ([vo_is_external,vo_is_thread_var]*sym.varoptions)<>[] then
  869. exit;
  870. writer.AsmWrite('.field ');
  871. writer.AsmWriteln(FieldDefinition(sym));
  872. end;
  873. procedure TJasminAssembler.WriteConstSym(sym: tconstsym);
  874. begin
  875. writer.AsmWrite('.field ');
  876. writer.AsmWriteln(ConstDefinition(sym));
  877. end;
  878. procedure TJasminAssembler.WriteSymtableVarSyms(st: TSymtable);
  879. var
  880. sym : tsym;
  881. i,j : longint;
  882. begin
  883. if not assigned(st) then
  884. exit;
  885. for i:=0 to st.SymList.Count-1 do
  886. begin
  887. sym:=tsym(st.SymList[i]);
  888. case sym.typ of
  889. staticvarsym,
  890. fieldvarsym:
  891. begin
  892. WriteFieldSym(tabstractvarsym(sym));
  893. if (sym.typ=staticvarsym) and
  894. assigned(tstaticvarsym(sym).defaultconstsym) then
  895. WriteFieldSym(tabstractvarsym(tstaticvarsym(sym).defaultconstsym));
  896. end;
  897. constsym:
  898. begin
  899. { multiple procedures can have constants with the same name }
  900. if not assigned(sym.owner.defowner) or
  901. (tdef(sym.owner.defowner).typ<>procdef) then
  902. WriteConstSym(tconstsym(sym));
  903. end;
  904. procsym:
  905. begin
  906. for j:=0 to tprocsym(sym).procdeflist.count-1 do
  907. if not(df_generic in tprocdef(tprocsym(sym).procdeflist[j]).defoptions) then
  908. WriteSymtableVarSyms(tprocdef(tprocsym(sym).procdeflist[j]).localst);
  909. end;
  910. end;
  911. end;
  912. end;
  913. procedure TJasminAssembler.WriteSymtableProcdefs(st: TSymtable);
  914. var
  915. i : longint;
  916. def : tdef;
  917. begin
  918. if not assigned(st) then
  919. exit;
  920. for i:=0 to st.DefList.Count-1 do
  921. begin
  922. def:=tdef(st.DefList[i]);
  923. case def.typ of
  924. procdef :
  925. begin
  926. { methods are also in the static/globalsymtable of the unit
  927. -> make sure they are only written for the objectdefs that
  928. own them }
  929. if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
  930. (def.owner=st)) and
  931. not(df_generic in def.defoptions) then
  932. begin
  933. WriteProcDef(tprocdef(def));
  934. if assigned(tprocdef(def).localst) then
  935. WriteSymtableProcdefs(tprocdef(def).localst);
  936. end;
  937. end;
  938. end;
  939. end;
  940. end;
  941. procedure TJasminAssembler.WriteSymtableStructDefs(st: TSymtable);
  942. var
  943. i : longint;
  944. def : tdef;
  945. obj : tabstractrecorddef;
  946. nestedstructs: tfpobjectlist;
  947. begin
  948. if not assigned(st) then
  949. exit;
  950. nestedstructs:=tfpobjectlist.create(false);
  951. for i:=0 to st.DefList.Count-1 do
  952. begin
  953. def:=tdef(st.DefList[i]);
  954. if df_generic in def.defoptions then
  955. continue;
  956. case def.typ of
  957. objectdef:
  958. if not(oo_is_external in tobjectdef(def).objectoptions) then
  959. nestedstructs.add(def);
  960. recorddef:
  961. nestedstructs.add(def);
  962. end;
  963. end;
  964. for i:=0 to nestedstructs.count-1 do
  965. begin
  966. obj:=tabstractrecorddef(nestedstructs[i]);
  967. NewAsmFileForStructDef(obj);
  968. WriteExtraHeader(obj);
  969. WriteSymtableVarSyms(obj.symtable);
  970. writer.AsmLn;
  971. WriteSymtableProcDefs(obj.symtable);
  972. WriteSymtableStructDefs(obj.symtable);
  973. end;
  974. nestedstructs.free;
  975. end;
  976. constructor TJasminAssembler.Create(info: pasminfo; smart: boolean);
  977. begin
  978. { this is a bit dirty: the "main" constructor is is this one, which is
  979. called by TExternalAssembler.CreateWithWriter(). That means the call
  980. below to CreateWithWriter will end up here again when it calls create.
  981. It will first initialise fwriter though, so we can check that field,
  982. and otherwise call the inherited create }
  983. if not assigned(writer) then
  984. begin
  985. CreateWithWriter(info,TJasminAssemblerOutputFile.Create(self),true,smart);
  986. InstrWriter:=TJasminInstrWriter.Create(self);
  987. asmfiles:=TCmdStrList.Create;
  988. end
  989. else
  990. inherited;
  991. end;
  992. procedure TJasminAssembler.WriteAsmList;
  993. begin
  994. {$ifdef EXTDEBUG}
  995. if assigned(current_module.mainsource) then
  996. Comment(V_Debug,'Start writing Jasmin-styled assembler output for '+current_module.mainsource);
  997. {$endif}
  998. writer.MarkEmpty;
  999. WriteExtraHeader(nil);
  1000. (*
  1001. for hal:=low(TasmlistType) to high(TasmlistType) do
  1002. begin
  1003. writer.AsmWriteLn(asminfo^.comment+'Begin asmlist '+AsmlistTypeStr[hal]);
  1004. writetree(current_asmdata.asmlists[hal]);
  1005. writer.AsmWriteLn(asminfo^.comment+'End asmlist '+AsmlistTypeStr[hal]);
  1006. end;
  1007. *)
  1008. { print all global variables }
  1009. WriteSymtableVarSyms(current_module.globalsymtable);
  1010. WriteSymtableVarSyms(current_module.localsymtable);
  1011. writer.AsmLn;
  1012. { print all global procedures/functions }
  1013. WriteSymtableProcdefs(current_module.globalsymtable);
  1014. WriteSymtableProcdefs(current_module.localsymtable);
  1015. WriteSymtableStructDefs(current_module.globalsymtable);
  1016. WriteSymtableStructDefs(current_module.localsymtable);
  1017. writer.AsmLn;
  1018. {$ifdef EXTDEBUG}
  1019. if assigned(current_module.mainsource) then
  1020. Comment(V_Debug,'Done writing gas-styled assembler output for '+current_module.mainsource);
  1021. {$endif EXTDEBUG}
  1022. end;
  1023. {****************************************************************************}
  1024. { Jasmin Instruction Writer }
  1025. {****************************************************************************}
  1026. constructor TJasminInstrWriter.create(_owner: TJasminAssembler);
  1027. begin
  1028. inherited create;
  1029. owner := _owner;
  1030. end;
  1031. function getreferencestring(var ref : treference) : ansistring;
  1032. begin
  1033. if (ref.arrayreftype<>art_none) or
  1034. (ref.index<>NR_NO) then
  1035. internalerror(2010122809);
  1036. if assigned(ref.symbol) then
  1037. begin
  1038. // global symbol or field -> full type and name
  1039. // ref.base can be <> NR_NO in case an instance field is loaded.
  1040. // This register is not part of this instruction, it will have
  1041. // been placed on the stack by the previous one.
  1042. if (ref.offset<>0) then
  1043. internalerror(2010122811);
  1044. result:=ref.symbol.name;
  1045. end
  1046. else
  1047. begin
  1048. // local symbol -> stack slot, stored in offset
  1049. if ref.base<>NR_STACK_POINTER_REG then
  1050. internalerror(2010122810);
  1051. result:=tostr(ref.offset);
  1052. end;
  1053. end;
  1054. function getopstr(const o:toper) : ansistring;
  1055. var
  1056. d: double;
  1057. s: single;
  1058. begin
  1059. case o.typ of
  1060. top_reg:
  1061. // should have been translated into a memory location by the
  1062. // register allocator)
  1063. if (cs_no_regalloc in current_settings.globalswitches) then
  1064. getopstr:=std_regname(o.reg)
  1065. else
  1066. internalerror(2010122803);
  1067. top_const:
  1068. str(o.val,result);
  1069. top_ref:
  1070. getopstr:=getreferencestring(o.ref^);
  1071. top_single:
  1072. begin
  1073. result:=constsingle(o.sval);
  1074. end;
  1075. top_double:
  1076. begin
  1077. result:=constdouble(o.dval);
  1078. end;
  1079. top_string:
  1080. begin
  1081. result:=constastr(o.pcval,o.pcvallen);
  1082. end;
  1083. top_wstring:
  1084. begin
  1085. result:=constwstr(o.pwstrval^.data,getlengthwidestring(o.pwstrval));
  1086. end
  1087. else
  1088. internalerror(2010122802);
  1089. end;
  1090. end;
  1091. procedure TJasminInstrWriter.WriteInstruction(hp: tai);
  1092. var
  1093. s: ansistring;
  1094. i: byte;
  1095. sep: ansistring;
  1096. begin
  1097. s:=#9+jas_op2str[taicpu(hp).opcode];
  1098. if taicpu(hp).ops<>0 then
  1099. begin
  1100. sep:=#9;
  1101. for i:=0 to taicpu(hp).ops-1 do
  1102. begin
  1103. s:=s+sep+getopstr(taicpu(hp).oper[i]^);
  1104. sep:=' ';
  1105. end;
  1106. end;
  1107. owner.writer.AsmWriteLn(s);
  1108. end;
  1109. {****************************************************************************}
  1110. { Jasmin Instruction Writer }
  1111. {****************************************************************************}
  1112. const
  1113. as_jvm_jasmin_info : tasminfo =
  1114. (
  1115. id : as_jvm_jasmin;
  1116. idtxt : 'Jasmin';
  1117. asmbin : 'java';
  1118. asmcmd : '-jar $JASMINJAR $ASM $EXTRAOPT -d $OBJDIR';
  1119. supported_targets : [system_jvm_java32,system_jvm_android32];
  1120. flags : [];
  1121. labelprefix : 'L';
  1122. comment : ' ; ';
  1123. dollarsign : '$';
  1124. );
  1125. begin
  1126. RegisterAssembler(as_jvm_jasmin_info,TJasminAssembler);
  1127. end.