mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-07-04 07:48:55 +02:00
335 lines
10 KiB
ObjectPascal
335 lines
10 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2020 by the Free Pascal team
|
|
|
|
This unit implements the llvm-mc ("llvm machine code playground")
|
|
assembler writer for WebAssembly
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
|
|
unit agllvmmc;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
systems,cgutils,
|
|
globtype,globals,
|
|
symbase,symdef,symtype,symconst,symcpu,
|
|
aasmbase,aasmtai,aasmdata,aasmcpu,
|
|
assemble,aggas;
|
|
|
|
type
|
|
|
|
{ TLLVMMachineCodePlaygroundAssembler }
|
|
|
|
TLLVMMachineCodePlaygroundAssembler=class(TGNUassembler)
|
|
protected
|
|
procedure WriteProcDef(pd: tprocdef);
|
|
procedure WriteSymtableProcdefs(st: TSymtable);
|
|
procedure WriteImports;
|
|
|
|
function sectionname(atype:TAsmSectiontype;const aname:string;aorder:TAsmSectionOrder):string;override;
|
|
public
|
|
constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
|
|
procedure WriteAsmList;override;
|
|
end;
|
|
|
|
{ TWASM32InstrWriter }
|
|
|
|
TWASM32InstrWriter = class(TCPUInstrWriter)
|
|
procedure WriteInstruction(hp : tai);override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
cutils,
|
|
fmodule,finput,
|
|
itcpugas,
|
|
cpubase,
|
|
hlcgobj,hlcgcpu,
|
|
verbose;
|
|
|
|
{ TLLVMMachineCodePlaygroundAssembler }
|
|
|
|
|
|
procedure TLLVMMachineCodePlaygroundAssembler.WriteProcDef(pd: tprocdef);
|
|
begin
|
|
if not assigned(tcpuprocdef(pd).exprasmlist) and
|
|
not(po_abstractmethod in pd.procoptions) and
|
|
(pd.proctypeoption in [potype_unitinit,potype_unitfinalize]) then
|
|
exit;
|
|
|
|
writer.AsmWriteLn(asminfo^.comment+'WriteProcDef('+pd.mangledname+')');
|
|
WriteTree(tcpuprocdef(pd).exprasmlist);
|
|
writer.AsmWriteLn(asminfo^.comment+'WriteProcDef('+pd.mangledname+') done');
|
|
end;
|
|
|
|
|
|
procedure TLLVMMachineCodePlaygroundAssembler.WriteSymtableProcdefs(st: TSymtable);
|
|
var
|
|
i : longint;
|
|
def : tdef;
|
|
begin
|
|
if not assigned(st) then
|
|
exit;
|
|
for i:=0 to st.DefList.Count-1 do
|
|
begin
|
|
def:=tdef(st.DefList[i]);
|
|
case def.typ of
|
|
procdef :
|
|
begin
|
|
{ methods are also in the static/globalsymtable of the unit
|
|
-> make sure they are only written for the objectdefs that
|
|
own them }
|
|
if (not(st.symtabletype in [staticsymtable,globalsymtable]) or
|
|
(def.owner=st)) and
|
|
not(df_generic in def.defoptions) then
|
|
begin
|
|
WriteProcDef(tprocdef(def));
|
|
if assigned(tprocdef(def).localst) then
|
|
WriteSymtableProcdefs(tprocdef(def).localst);
|
|
end;
|
|
end;
|
|
else
|
|
;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TLLVMMachineCodePlaygroundAssembler.WriteImports;
|
|
var
|
|
i : integer;
|
|
proc : tprocdef;
|
|
list : TAsmList;
|
|
begin
|
|
for i:=0 to current_module.deflist.Count-1 do
|
|
if tdef(current_module.deflist[i]).typ = procdef then
|
|
begin
|
|
proc := tprocdef(current_module.deflist[i]);
|
|
if (po_external in proc.procoptions) and assigned(proc.import_dll) then
|
|
begin
|
|
//WriteProcDef(proc);
|
|
list:=TAsmList.Create;
|
|
thlcgwasm(hlcg).g_procdef(list,proc);
|
|
WriteTree(list);
|
|
list.free;
|
|
writer.AsmWrite(#9'.import_module'#9);
|
|
writer.AsmWrite(proc.mangledname);
|
|
writer.AsmWrite(', ');
|
|
writer.AsmWriteLn(proc.import_dll^);
|
|
writer.AsmWrite(#9'.import_name'#9);
|
|
writer.AsmWrite(proc.mangledname);
|
|
writer.AsmWrite(', ');
|
|
writer.AsmWriteLn(proc.import_name^);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TLLVMMachineCodePlaygroundAssembler.sectionname(atype: TAsmSectiontype; const aname: string; aorder: TAsmSectionOrder): string;
|
|
begin
|
|
if atype=sec_fpc then
|
|
atype:=sec_data;
|
|
Result:=inherited sectionname(atype, aname, aorder)+',"",@';
|
|
end;
|
|
|
|
|
|
constructor TLLVMMachineCodePlaygroundAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
|
|
begin
|
|
inherited;
|
|
InstrWriter:=TWASM32InstrWriter.create(self);
|
|
end;
|
|
|
|
|
|
procedure TLLVMMachineCodePlaygroundAssembler.WriteAsmList;
|
|
begin
|
|
inherited;
|
|
{ print all global procedures/functions }
|
|
WriteSymtableProcdefs(current_module.globalsymtable);
|
|
WriteSymtableProcdefs(current_module.localsymtable);
|
|
writer.AsmWriteLn(#9'.globaltype'#9+STACK_POINTER_SYM+', i32');
|
|
WriteImports;
|
|
end;
|
|
|
|
|
|
{ TWASM32InstrWriter }
|
|
|
|
|
|
procedure TWASM32InstrWriter.WriteInstruction(hp: tai);
|
|
|
|
function getreferencestring(var ref : treference) : ansistring;
|
|
begin
|
|
if (ref.index<>NR_NO) then
|
|
internalerror(2010122809);
|
|
if assigned(ref.symbol) then
|
|
begin
|
|
// global symbol or field -> full type and name
|
|
// ref.base can be <> NR_NO in case an instance field is loaded.
|
|
// This register is not part of this instruction, it will have
|
|
// been placed on the stack by the previous one.
|
|
result:=ref.symbol.name;
|
|
if ref.offset>0 then
|
|
result:=result+'+'+tostr(ref.offset)
|
|
else if ref.offset<0 then
|
|
result:=result+tostr(ref.offset);
|
|
end
|
|
else
|
|
begin
|
|
// local symbol -> stack slot, stored in offset
|
|
if ref.base<>NR_STACK_POINTER_REG then
|
|
internalerror(2010122810);
|
|
result:=tostr(ref.offset);
|
|
end;
|
|
end;
|
|
|
|
function constsingle(s: single): ansistring;
|
|
begin
|
|
// wat2wasm is using strtof() internally
|
|
str(s, result); //'0x'+hexstr(longint(t32bitarray(s)),8);
|
|
end;
|
|
|
|
function constdouble(d: double): ansistring;
|
|
begin
|
|
// force interpretation as double (since we write it out as an
|
|
// integer, we never have to swap the endianess). We have to
|
|
// include the sign separately because of the way Java parses
|
|
// hex numbers (0x8000000000000000 is not a valid long)
|
|
//result:=hexstr(abs(int64(t64bitarray(d))),16);
|
|
//if int64(t64bitarray(d))<0 then
|
|
// result:='-'+result;
|
|
//result:='0dx'+result;
|
|
str(d, result);
|
|
end;
|
|
|
|
function getopstr(const o:toper) : ansistring;
|
|
var
|
|
d: double;
|
|
s: single;
|
|
begin
|
|
case o.typ of
|
|
top_reg:
|
|
// should have been translated into a memory location by the
|
|
// register allocator)
|
|
if (cs_no_regalloc in current_settings.globalswitches) then
|
|
getopstr:=std_regname(o.reg)
|
|
else
|
|
internalerror(2010122803);
|
|
top_const:
|
|
str(o.val,result);
|
|
top_ref:
|
|
getopstr:=getreferencestring(o.ref^);
|
|
top_single:
|
|
begin
|
|
result:=constsingle(o.sval);
|
|
end;
|
|
top_double:
|
|
begin
|
|
result:=constdouble(o.dval);
|
|
end;
|
|
{top_string:
|
|
begin
|
|
result:=constastr(o.pcval,o.pcvallen);
|
|
end;
|
|
top_wstring:
|
|
begin
|
|
result:=constwstr(o.pwstrval^.data,getlengthwidestring(o.pwstrval));
|
|
end}
|
|
else
|
|
internalerror(2010122802);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
cpu : taicpu;
|
|
i : integer;
|
|
isprm : boolean;
|
|
writer: TExternalAssemblerOutputFile;
|
|
begin
|
|
writer:=owner.writer;
|
|
cpu := taicpu(hp);
|
|
writer.AsmWrite(#9#9);
|
|
writer.AsmWrite(gas_op2str[cpu.opcode]);
|
|
|
|
if (cpu.opcode = a_call_indirect) then begin
|
|
// special wat2wasm syntax "call_indirect (type x)"
|
|
writer.AsmWrite(#9);
|
|
isprm := true;
|
|
for i:=1 to length(cpu.typecode) do
|
|
if cpu.typecode[i]=':' then
|
|
isprm:=false
|
|
else begin
|
|
if isprm then writer.AsmWrite('(param ')
|
|
else writer.AsmWrite('(result ');
|
|
case cpu.typecode[i] of
|
|
'i': writer.AsmWrite('i32');
|
|
'I': writer.AsmWrite('i64');
|
|
'f': writer.AsmWrite('f32');
|
|
'F': writer.AsmWrite('f64');
|
|
end;
|
|
writer.AsmWrite(')');
|
|
end;
|
|
writer.AsmLn;
|
|
exit;
|
|
end;
|
|
|
|
|
|
if (cpu.opcode = a_if) then
|
|
writer.AsmWrite(' (result i32)') //todo: this is a hardcode, but shouldn't
|
|
else
|
|
|
|
cpu := taicpu(hp);
|
|
if cpu.ops<>0 then
|
|
begin
|
|
for i:=0 to cpu.ops-1 do
|
|
begin
|
|
writer.AsmWrite(#9);
|
|
writer.AsmWrite(getopstr(cpu.oper[i]^));
|
|
end;
|
|
end;
|
|
|
|
if (cpu.opcode = a_call_indirect) then
|
|
// special wat2wasm syntax "call_indirect (type x)"
|
|
writer.AsmWrite(')');
|
|
|
|
writer.AsmLn;
|
|
end;
|
|
|
|
|
|
const
|
|
as_wasm32_llvm_mc_info : tasminfo =
|
|
(
|
|
id : as_wasm32_llvm_mc;
|
|
idtxt : 'LLVM-MC';
|
|
asmbin : 'llvm-mc';
|
|
asmcmd : '--assemble --arch=wasm32 --filetype=obj -o $OBJ $EXTRAOPT $ASM';
|
|
supported_targets : [system_wasm32_wasm,system_wasm32_wasi];
|
|
flags : [];
|
|
labelprefix : '.L';
|
|
labelmaxlen : -1;
|
|
comment : '# ';
|
|
dollarsign : '$';
|
|
);
|
|
|
|
initialization
|
|
RegisterAssembler(as_wasm32_llvm_mc_info,TLLVMMachineCodePlaygroundAssembler);
|
|
end.
|
|
|