agbinaryen.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. {
  2. Copyright (c) 2017 by Karoly Balogh
  3. This unit implements the Binaryen 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 agbinaryen;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,systems,
  22. globtype,globals,
  23. symconst,symbase,symdef,symsym,
  24. aasmbase,aasmtai,aasmdata,aasmcpu,
  25. assemble;
  26. type
  27. TBinaryenAssemblerOutputFile=class(TExternalAssemblerOutputFile)
  28. procedure RemoveAsm; override;
  29. end;
  30. TBinaryenInstrWriter = class;
  31. {# This is a derived class which is used to write
  32. Binaryen-styled assembler.
  33. }
  34. { TBinaryenAssembler }
  35. TBinaryenAssembler=class(texternalassembler)
  36. protected
  37. jasminjar: tcmdstr;
  38. asmfiles: TCmdStrList;
  39. procedure WriteExtraHeader(obj: tabstractrecorddef);
  40. procedure WriteInstruction(hp: tai);
  41. function CreateNewAsmWriter: TExternalAssemblerOutputFile; override;
  42. public
  43. constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
  44. function MakeCmdLine: TCmdStr;override;
  45. procedure WriteTree(p:TAsmList);override;
  46. procedure WriteAsmList;override;
  47. destructor destroy; override;
  48. protected
  49. InstrWriter: TBinaryenInstrWriter;
  50. end;
  51. {# This is the base class for writing instructions.
  52. The WriteInstruction() method must be overridden
  53. to write a single instruction to the assembler
  54. file.
  55. }
  56. { TBinaryenInstrWriter }
  57. TBinaryenInstrWriter = class
  58. constructor create(_owner: TBinaryenAssembler);
  59. procedure WriteInstruction(hp : tai); virtual;
  60. protected
  61. owner: TBinaryenAssembler;
  62. end;
  63. implementation
  64. uses
  65. SysUtils,
  66. cutils,cfileutl,cscript,
  67. fmodule,finput,verbose,
  68. symtype,symcpu,symtable,
  69. itcpuwasm,cpubase,cpuinfo,cgutils,
  70. widestr
  71. ;
  72. const
  73. line_length = 70;
  74. type
  75. t64bitarray = array[0..7] of byte;
  76. t32bitarray = array[0..3] of byte;
  77. {****************************************************************************}
  78. { Support routines }
  79. {****************************************************************************}
  80. function fixline(s:string):string;
  81. {
  82. return s with all leading and ending spaces and tabs removed
  83. }
  84. var
  85. i,j,k : integer;
  86. begin
  87. i:=length(s);
  88. while (i>0) and (s[i] in [#9,' ']) do
  89. dec(i);
  90. j:=1;
  91. while (j<i) and (s[j] in [#9,' ']) do
  92. inc(j);
  93. for k:=j to i do
  94. if s[k] in [#0..#31,#127..#255] then
  95. s[k]:='.';
  96. fixline:=Copy(s,j,i-j+1);
  97. end;
  98. function constastr(p: pchar; len: longint): ansistring;
  99. var
  100. i,runstart,runlen: longint;
  101. procedure flush;
  102. begin
  103. if runlen>0 then
  104. begin
  105. setlength(result,length(result)+runlen);
  106. move(p[runstart],result[length(result)-runlen+1],runlen);
  107. runlen:=0;
  108. end;
  109. end;
  110. begin
  111. result:='"';
  112. runlen:=0;
  113. runstart:=0;
  114. for i:=0 to len-1 do
  115. begin
  116. { escape control codes }
  117. case p[i] of
  118. { LF and CR must be escaped specially, because \uXXXX parsing
  119. happens in the pre-processor, so it's the same as actually
  120. inserting a newline in the middle of a string constant }
  121. #10:
  122. begin
  123. flush;
  124. result:=result+'\n';
  125. end;
  126. #13:
  127. begin
  128. flush;
  129. result:=result+'\r';
  130. end;
  131. '"','\':
  132. begin
  133. flush;
  134. result:=result+'\'+p[i];
  135. end
  136. else if p[i]<#32 then
  137. begin
  138. flush;
  139. result:=result+'\u'+hexstr(ord(p[i]),4);
  140. end
  141. else if p[i]<#127 then
  142. begin
  143. if runlen=0 then
  144. runstart:=i;
  145. inc(runlen);
  146. end
  147. else
  148. begin
  149. { see comments in njvmcon }
  150. flush;
  151. result:=result+'\u'+hexstr(ord(p[i]),4)
  152. end;
  153. end;
  154. end;
  155. flush;
  156. result:=result+'"';
  157. end;
  158. {****************************************************************************}
  159. { Binaryen Output File }
  160. {****************************************************************************}
  161. procedure TBinaryenAssemblerOutputFile.RemoveAsm;
  162. var
  163. g : file;
  164. begin
  165. inherited;
  166. if cs_asm_leave in current_settings.globalswitches then
  167. exit;
  168. while not TBinaryenAssembler(owner).asmfiles.empty do
  169. begin
  170. if cs_asm_extern in current_settings.globalswitches then
  171. AsmRes.AddDeleteCommand(TBinaryenAssembler(owner).asmfiles.GetFirst)
  172. else
  173. begin
  174. assign(g,TBinaryenAssembler(owner).asmfiles.GetFirst);
  175. {$I-}
  176. erase(g);
  177. {$I+}
  178. if ioresult<>0 then;
  179. end;
  180. end;
  181. end;
  182. {****************************************************************************}
  183. { Binaryen Assembler writer }
  184. {****************************************************************************}
  185. destructor TBinaryenAssembler.Destroy;
  186. begin
  187. InstrWriter.free;
  188. asmfiles.free;
  189. inherited destroy;
  190. end;
  191. procedure TBinaryenAssembler.WriteTree(p:TAsmList);
  192. var
  193. ch : char;
  194. hp : tai;
  195. hp1 : tailineinfo;
  196. s : ansistring;
  197. i,pos : longint;
  198. InlineLevel : longint;
  199. do_line : boolean;
  200. begin
  201. if not assigned(p) then
  202. exit;
  203. InlineLevel:=0;
  204. { lineinfo is only needed for al_procedures (PFV) }
  205. do_line:=(cs_asm_source in current_settings.globalswitches);
  206. hp:=tai(p.first);
  207. while assigned(hp) do
  208. begin
  209. prefetch(pointer(hp.next)^);
  210. if not(hp.typ in SkipLineInfo) then
  211. begin
  212. hp1 := hp as tailineinfo;
  213. current_filepos:=hp1.fileinfo;
  214. { no line info for inlined code }
  215. if do_line and (inlinelevel=0) then
  216. begin
  217. { load infile }
  218. if lastfileinfo.fileindex<>hp1.fileinfo.fileindex then
  219. begin
  220. infile:=current_module.sourcefiles.get_file(hp1.fileinfo.fileindex);
  221. if assigned(infile) then
  222. begin
  223. { open only if needed !! }
  224. if (cs_asm_source in current_settings.globalswitches) then
  225. infile.open;
  226. end;
  227. { avoid unnecessary reopens of the same file !! }
  228. lastfileinfo.fileindex:=hp1.fileinfo.fileindex;
  229. { be sure to change line !! }
  230. lastfileinfo.line:=-1;
  231. end;
  232. { write source }
  233. if (cs_asm_source in current_settings.globalswitches) and
  234. assigned(infile) then
  235. begin
  236. if (infile<>lastinfile) then
  237. begin
  238. writer.AsmWriteLn(asminfo^.comment+'['+infile.name+']');
  239. if assigned(lastinfile) then
  240. lastinfile.close;
  241. end;
  242. if (hp1.fileinfo.line<>lastfileinfo.line) and
  243. ((hp1.fileinfo.line<infile.maxlinebuf) or (InlineLevel>0)) then
  244. begin
  245. if (hp1.fileinfo.line<>0) and
  246. ((infile.linebuf^[hp1.fileinfo.line]>=0) or (InlineLevel>0)) then
  247. writer.AsmWriteLn(asminfo^.comment+'['+tostr(hp1.fileinfo.line)+'] '+
  248. fixline(infile.GetLineStr(hp1.fileinfo.line)));
  249. { set it to a negative value !
  250. to make that is has been read already !! PM }
  251. if (infile.linebuf^[hp1.fileinfo.line]>=0) then
  252. infile.linebuf^[hp1.fileinfo.line]:=-infile.linebuf^[hp1.fileinfo.line]-1;
  253. end;
  254. end;
  255. lastfileinfo:=hp1.fileinfo;
  256. lastinfile:=infile;
  257. end;
  258. end;
  259. case hp.typ of
  260. ait_comment :
  261. Begin
  262. writer.AsmWrite(asminfo^.comment);
  263. writer.AsmWritePChar(tai_comment(hp).str);
  264. writer.AsmLn;
  265. End;
  266. ait_regalloc :
  267. begin
  268. if (cs_asm_regalloc in current_settings.globalswitches) then
  269. begin
  270. writer.AsmWrite(#9+asminfo^.comment+'Register ');
  271. repeat
  272. writer.AsmWrite(std_regname(Tai_regalloc(hp).reg));
  273. if (hp.next=nil) or
  274. (tai(hp.next).typ<>ait_regalloc) or
  275. (tai_regalloc(hp.next).ratype<>tai_regalloc(hp).ratype) then
  276. break;
  277. hp:=tai(hp.next);
  278. writer.AsmWrite(',');
  279. until false;
  280. writer.AsmWrite(' ');
  281. writer.AsmWriteLn(regallocstr[tai_regalloc(hp).ratype]);
  282. end;
  283. end;
  284. ait_tempalloc :
  285. begin
  286. if (cs_asm_tempalloc in current_settings.globalswitches) then
  287. begin
  288. {$ifdef EXTDEBUG}
  289. if assigned(tai_tempalloc(hp).problem) then
  290. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  291. tostr(tai_tempalloc(hp).tempsize)+' '+tai_tempalloc(hp).problem^)
  292. else
  293. {$endif EXTDEBUG}
  294. writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(tai_tempalloc(hp).temppos)+','+
  295. tostr(tai_tempalloc(hp).tempsize)+' '+tempallocstr[tai_tempalloc(hp).allocation]);
  296. end;
  297. end;
  298. ait_align :
  299. begin
  300. end;
  301. ait_section :
  302. begin
  303. end;
  304. ait_datablock :
  305. begin
  306. // internalerror(2010122701);
  307. end;
  308. ait_const:
  309. begin
  310. writer.AsmWriteln('constant');
  311. // internalerror(2010122702);
  312. end;
  313. ait_realconst :
  314. begin
  315. internalerror(2010122703);
  316. end;
  317. ait_string :
  318. begin
  319. pos:=0;
  320. for i:=1 to tai_string(hp).len do
  321. begin
  322. if pos=0 then
  323. begin
  324. writer.AsmWrite(#9'strconst: '#9'"');
  325. pos:=20;
  326. end;
  327. ch:=tai_string(hp).str[i-1];
  328. case ch of
  329. #0, {This can't be done by range, because a bug in FPC}
  330. #1..#31,
  331. #128..#255 : s:='\'+tostr(ord(ch) shr 6)+tostr((ord(ch) and 63) shr 3)+tostr(ord(ch) and 7);
  332. '"' : s:='\"';
  333. '\' : s:='\\';
  334. else
  335. s:=ch;
  336. end;
  337. writer.AsmWrite(s);
  338. inc(pos,length(s));
  339. if (pos>line_length) or (i=tai_string(hp).len) then
  340. begin
  341. writer.AsmWriteLn('"');
  342. pos:=0;
  343. end;
  344. end;
  345. end;
  346. ait_label :
  347. begin
  348. if (tai_label(hp).labsym.is_used) then
  349. begin
  350. writer.AsmWrite(tai_label(hp).labsym.name);
  351. writer.AsmWriteLn(':');
  352. end;
  353. end;
  354. ait_symbol :
  355. begin
  356. if (tai_symbol(hp).sym.typ = AT_FUNCTION) then
  357. begin
  358. end
  359. else
  360. begin
  361. writer.AsmWrite('data symbol: ');
  362. writer.AsmWriteln(tai_symbol(hp).sym.name);
  363. // internalerror(2010122706);
  364. end;
  365. end;
  366. ait_symbol_end :
  367. begin
  368. end;
  369. ait_instruction :
  370. begin
  371. WriteInstruction(hp);
  372. end;
  373. ait_force_line,
  374. ait_function_name : ;
  375. ait_cutobject :
  376. begin
  377. end;
  378. ait_marker :
  379. if tai_marker(hp).kind=mark_NoLineInfoStart then
  380. inc(InlineLevel)
  381. else if tai_marker(hp).kind=mark_NoLineInfoEnd then
  382. dec(InlineLevel);
  383. ait_directive :
  384. begin
  385. { the CPU directive is probably not supported by the JVM assembler,
  386. so it's commented out }
  387. if tai_directive(hp).directive=asd_cpu then
  388. writer.AsmWrite(asminfo^.comment);
  389. writer.AsmWrite('.'+directivestr[tai_directive(hp).directive]+' ');
  390. if tai_directive(hp).name<>'' then
  391. writer.AsmWrite(tai_directive(hp).name);
  392. writer.AsmLn;
  393. end;
  394. else
  395. internalerror(2010122707);
  396. end;
  397. hp:=tai(hp.next);
  398. end;
  399. end;
  400. procedure TBinaryenAssembler.WriteExtraHeader(obj: tabstractrecorddef);
  401. begin
  402. end;
  403. procedure TBinaryenAssembler.WriteInstruction(hp: tai);
  404. begin
  405. InstrWriter.WriteInstruction(hp);
  406. end;
  407. function TBinaryenAssembler.MakeCmdLine: TCmdStr;
  408. begin
  409. Replace(result,'$EXTRAOPT',asmextraopt);
  410. end;
  411. function TBinaryenAssembler.CreateNewAsmWriter: TExternalAssemblerOutputFile;
  412. begin
  413. Result:=TBinaryenAssemblerOutputFile.Create(self);
  414. end;
  415. constructor TBinaryenAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
  416. begin
  417. inherited;
  418. InstrWriter:=TBinaryenInstrWriter.Create(self);
  419. asmfiles:=TCmdStrList.Create;
  420. end;
  421. procedure TBinaryenAssembler.WriteAsmList;
  422. var
  423. hal : tasmlisttype;
  424. begin
  425. writer.MarkEmpty;
  426. WriteExtraHeader(nil);
  427. for hal:=low(TasmlistType) to high(TasmlistType) do
  428. begin
  429. if not (current_asmdata.asmlists[hal].empty) then
  430. begin
  431. writer.AsmWriteLn(asminfo^.comment+'Begin asmlist '+AsmlistTypeStr[hal]);
  432. writetree(current_asmdata.asmlists[hal]);
  433. writer.AsmWriteLn(asminfo^.comment+'End asmlist '+AsmlistTypeStr[hal]);
  434. end;
  435. end;
  436. writer.AsmLn;
  437. end;
  438. {****************************************************************************}
  439. { Binaryen Instruction Writer }
  440. {****************************************************************************}
  441. constructor TBinaryenInstrWriter.create(_owner: TBinaryenAssembler);
  442. begin
  443. inherited create;
  444. owner := _owner;
  445. end;
  446. function getreferencestring(var ref : treference) : ansistring;
  447. begin
  448. { if (ref.arrayreftype<>art_none) or
  449. (ref.index<>NR_NO) then
  450. internalerror(2010122809);}
  451. if assigned(ref.symbol) then
  452. begin
  453. // global symbol or field -> full type and name
  454. // ref.base can be <> NR_NO in case an instance field is loaded.
  455. // This register is not part of this instruction, it will have
  456. // been placed on the stack by the previous one.
  457. if (ref.offset<>0) then
  458. internalerror(2010122811);
  459. result:=ref.symbol.name;
  460. end
  461. else
  462. begin
  463. // local symbol -> stack slot, stored in offset
  464. if ref.base<>NR_STACK_POINTER_REG then
  465. internalerror(2010122810);
  466. result:=tostr(ref.offset);
  467. end;
  468. end;
  469. function getopstr(const o:toper) : ansistring;
  470. begin
  471. case o.typ of
  472. top_reg:
  473. // should have been translated into a memory location by the
  474. // register allocator)
  475. if (cs_no_regalloc in current_settings.globalswitches) then
  476. getopstr:=std_regname(o.reg)
  477. else
  478. internalerror(2010122803);
  479. top_const:
  480. str(o.val,result);
  481. top_ref:
  482. getopstr:=getreferencestring(o.ref^);
  483. else
  484. internalerror(2010122802);
  485. end;
  486. end;
  487. procedure TBinaryenInstrWriter.WriteInstruction(hp: tai);
  488. var
  489. s: ansistring;
  490. i: byte;
  491. sep: ansistring;
  492. begin
  493. s:=#9+wasm_op2str[taicpu(hp).opcode];
  494. if taicpu(hp).ops<>0 then
  495. begin
  496. sep:=#9;
  497. for i:=0 to taicpu(hp).ops-1 do
  498. begin
  499. s:=s+sep+getopstr(taicpu(hp).oper[i]^);
  500. sep:=' ';
  501. end;
  502. end;
  503. owner.writer.AsmWriteLn(s);
  504. end;
  505. {****************************************************************************}
  506. { Binaryen Instruction Writer }
  507. {****************************************************************************}
  508. const
  509. as_wasm_binaryen_info : tasminfo =
  510. (
  511. id : as_wasm_binaryen;
  512. idtxt : 'Binaryen';
  513. asmbin : 'wasm-as';
  514. asmcmd : '$ASM $EXTRAOPT';
  515. supported_targets : [system_wasm_wasm32];
  516. flags : [];
  517. labelprefix : 'L';
  518. comment : ';; ';
  519. dollarsign : '$';
  520. );
  521. begin
  522. RegisterAssembler(as_wasm_binaryen_info,TBinaryenAssembler);
  523. end.