fpc/compiler/aarch64/agcpugas.pas

928 lines
34 KiB
ObjectPascal

{
Copyright (c) 2003,2014 by Florian Klaempfl and Jonas Maebe
This unit implements an asm for AArch64
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.
****************************************************************************
}
{ This unit implements the GNU Assembler writer for AArch64
}
unit agcpugas;
{$i fpcdefs.inc}
interface
uses
globtype,systems,
aasmtai,aasmdata,aasmbase,
assemble,aggas,
cpubase,cpuinfo;
type
TAArch64InstrWriter=class(TCPUInstrWriter)
procedure WriteInstruction(hp : tai);override;
end;
TAArch64Assembler=class(TGNUassembler)
constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
function MakeCmdLine: TCmdStr; override;
end;
TAArch64AppleAssembler=class(TAppleGNUassembler)
constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
function MakeCmdLine: TCmdStr; override;
end;
TAArch64ClangGASAssembler=class(TAArch64Assembler)
private
procedure TransformSEHDirectives(list:TAsmList);
protected
function sectionflags(secflags:TSectionFlags):string;override;
public
function MakeCmdLine: TCmdStr; override;
procedure WriteAsmList; override;
end;
const
gas_shiftmode2str : array[tshiftmode] of string[4] = (
'','lsl','lsr','asr','ror',
'uxtb','uxth','uxtw','uxtx',
'sxtb','sxth','sxtw','sxtx');
const
cputype_to_gas_march : array[tcputype] of string = (
'', // cpu_none
'armv8-a', // armv8 is not accepted by GNU assembler
'armv8-a',
'armv8.1-a',
'armv8.2-a',
'armv8.3-a',
'armv8.4-a',
'armv8.5-a',
'armv8.6-a',
'armv8.7-a',
'armv8.8-a',
'armv8.9-a'
);
cputype_to_clang_march : array[tcputype] of string = (
'', // cpu_none
'armv8',
'armv8-a',
'armv8.1-a',
'armv8.2-a',
'armv8.3-a',
'armv8.4-a',
'armv8.5-a',
'armv8.6-a',
'armv8.7-a',
'armv8.8-a',
'armv8.9-a'
);
implementation
uses
cutils,cclasses,globals,verbose,
aasmcpu,
itcpugas,
cgbase,cgutils;
function GetmarchStr : String;
var
cf: tcpuflags;
begin
Result:='-march='+cputype_to_gas_march[current_settings.cputype];
for cf in cpu_capabilities[current_settings.cputype] do
begin
case cf of
CPUAARCH64_HAS_DOTPROD:
Result:=Result+'+dotprod';
CPUAARCH64_HAS_AES:
Result:=Result+'+aes';
CPUAARCH64_HAS_SHA2:
Result:=Result+'+sha2';
CPUAARCH64_HAS_SHA3:
Result:=Result+'+sha3';
CPUAARCH64_HAS_SM4:
Result:=Result+'+sm4';
CPUAARCH64_HAS_CRYPTO:
Result:=Result+'+crypto';
CPUAARCH64_HAS_MEMTAG:
Result:=Result+'+memtag';
CPUAARCH64_HAS_PAUTH:
{ currently, clang on aarch64-darwin does not support pauth and as
it is actually part of -march==armv8.4-a (and higher) we actually don't need it,
so ignore it at this point }
if not(current_settings.cputype>=cpu_armv84a) then
Result:=Result+'+pauth';
CPUAARCH64_HAS_TME:
Result:=Result+'+tme';
CPUAARCH64_HAS_PROFILE:
Result:=Result+'+profile';
CPUAARCH64_HAS_CSSC:
Result:=Result+'+cssc';
else
;
end
end;
end;
{****************************************************************************}
{ AArch64 Assembler writer }
{****************************************************************************}
constructor TAArch64Assembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
begin
inherited;
InstrWriter := TAArch64InstrWriter.create(self);
end;
function TAArch64Assembler.MakeCmdLine: TCmdStr;
begin
result:=inherited MakeCmdLine;
if cputype_to_gas_march[current_settings.cputype] <> '' then
Replace(result,'$MARCHOPT',GetmarchStr)
else
Replace(result,'$MARCHOPT','');
end;
{****************************************************************************}
{ Apple AArch64 Assembler writer }
{****************************************************************************}
constructor TAArch64AppleAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
begin
inherited;
InstrWriter := TAArch64InstrWriter.create(self);
end;
function TAArch64AppleAssembler.MakeCmdLine: TCmdStr;
begin
result:=inherited MakeCmdLine;
if cputype_to_gas_march[current_settings.cputype] <> '' then
Replace(result,'$MARCHOPT',GetmarchStr)
else
Replace(result,'$MARCHOPT','');
end;
{****************************************************************************}
{ CLang AArch64 Assembler writer }
{****************************************************************************}
function TAArch64ClangGASAssembler.MakeCmdLine: TCmdStr;
begin
result:=inherited MakeCmdLine;
end;
procedure TAArch64ClangGASAssembler.TransformSEHDirectives(list:TAsmList);
function convert_unwinddata(list:tasmlist):tdynamicarray;
procedure check_offset(ofs,max:dword);
begin
if ((ofs and $7)<>0) or (ofs>max) then
internalerror(2020041210);
end;
procedure check_reg(reg:tregister;rt:TRegisterType;min:TSuperRegister);
begin
if (getregtype(reg)<>rt) or (getsupreg(reg)<min) then
internalerror(2020041211);
end;
procedure writebyte(b:byte); inline;
begin
result.write(b,sizeof(b));
end;
procedure writeword(w:word);
begin
w:=NtoBE(w);
result.write(w,sizeof(w));
end;
procedure writedword(dw:dword);
begin
dw:=NtoBE(dw);
result.write(dw,sizeof(dw));
end;
const
min_int_reg = 19;
min_mm_reg = 8;
var
hp : tai;
seh : tai_seh_directive absolute hp;
begin
result:=tdynamicarray.create(0);
hp:=tai(list.last);
while assigned(hp) do
begin
if hp.typ<>ait_seh_directive then
internalerror(2020041502);
case seh.kind of
ash_stackalloc:
begin
if (seh.data.offset and $f)<>0 then
internalerror(2020041207);
if seh.data.offset<((1 shl 5)*16) then
writebyte(byte(seh.data.offset shr 4))
else if seh.data.offset<((1 shl 11)*16) then
writeword($C000 or word(seh.data.offset shr 4))
else if seh.data.offset<((1 shl 24)*16) then
writedword($E0000000 or (seh.data.offset shr 4))
else begin
writeln(hexstr(seh.data.offset,8));
internalerror(2020041209);
end;
end;
ash_addfp:
begin
check_offset(seh.data.offset,(1 shl 7)*8);
writeword($E200 or (seh.data.offset shr 3));
end;
ash_setfp:
writebyte($E1);
ash_nop:
writebyte($E3);
ash_savefplr:
begin
check_offset(seh.data.offset,504);
writebyte($40 or (seh.data.offset shr 3));
end;
ash_savefplr_x:
begin
check_offset(seh.data.offset,512);
writebyte($80 or (seh.data.offset shr 3)-1);
end;
ash_savereg:
begin
check_offset(seh.data.offset,504);
check_reg(seh.data.reg,R_INTREGISTER,min_int_reg);
writeword($D000 or ((getsupreg(seh.data.reg)-min_int_reg) shl 6) or (seh.data.offset shr 3));
end;
ash_savereg_x:
begin
check_offset(seh.data.offset,256);
check_reg(seh.data.reg,R_INTREGISTER,min_int_reg);
writeword($D400 or ((getsupreg(seh.data.reg)-min_int_reg) shl 5) or ((seh.data.offset shr 3)-1));
end;
ash_saveregp:
begin
check_offset(seh.data.offset,504);
check_reg(seh.data.reg,R_INTREGISTER,min_int_reg);
writeword($C800 or ((getsupreg(seh.data.reg)-min_int_reg) shl 6) or (seh.data.offset shr 3));
end;
ash_saveregp_x:
begin
check_offset(seh.data.offset,512);
check_reg(seh.data.reg,R_INTREGISTER,min_int_reg);
writeword($CC00 or ((getsupreg(seh.data.reg)-min_int_reg) shl 6) or ((seh.data.offset shr 3)-1));
end;
ash_savefreg:
begin
check_offset(seh.data.offset,504);
check_reg(seh.data.reg,R_MMREGISTER,min_mm_reg);
writeword($DC00 or ((getsupreg(seh.data.reg)-min_mm_reg) shl 6) or (seh.data.offset shr 3));
end;
ash_savefreg_x:
begin
check_offset(seh.data.offset,256);
check_reg(seh.data.reg,R_MMREGISTER,min_mm_reg);
writeword($DE00 or ((getsupreg(seh.data.reg)-min_mm_reg) shl 5) or ((seh.data.offset shr 3)-1));
end;
ash_savefregp:
begin
check_offset(seh.data.offset,504);
check_reg(seh.data.reg,R_MMREGISTER,min_mm_reg);
writeword($D800 or ((getsupreg(seh.data.reg)-min_mm_reg) shl 6) or (seh.data.offset shr 3));
end;
ash_savefregp_x:
begin
check_offset(seh.data.offset,512);
check_reg(seh.data.reg,R_MMREGISTER,min_mm_reg);
writeword($DA00 or ((getsupreg(seh.data.reg)-min_mm_reg) shl 6) or ((seh.data.offset shr 3)-1));
end;
else
internalerror(2020041503);
end;
hp:=tai(hp.previous);
end;
end;
var
unwinddata : tdynamicarray;
procedure writebyte(b:byte);
begin
unwinddata.write(b,sizeof(b));
end;
var
hp,hpnext,hpdata : tai;
seh : tai_seh_directive absolute hp;
lastsym : tai_symbol;
lastsec : tai_section;
inprologue,
deleteai : boolean;
totalcount,
instrcount,
datacount : sizeint;
handlername : tsymstr;
handlerflags : byte;
handlerdata : array of tai;
handlerdataidx : sizeint;
handlerdatacount : tai;
sehlist,
tmplist : TAsmList;
xdatasym : tasmsymbol;
unwindrec : longword;
begin
if not assigned(list) then
exit;
lastsym:=nil;
tmplist:=nil;
sehlist:=nil;
lastsec:=nil;
instrcount:=0;
datacount:=0;
unwinddata:=nil;
inprologue:=false;
handlerdata:=nil;
handlerdataidx:=0;
handlerdatacount:=nil;
handlerflags:=0;
handlername:='';
hp:=tai(list.first);
while assigned(hp) do
begin
deleteai:=false;
case hp.typ of
ait_section:
begin
if assigned(sehlist) then
begin
if assigned(lastsec) and (tai_section(hp).name^=lastsec.name^) then
begin
{ this section was only added due to the now removed SEH data }
deleteai:=true;
dec(list.section_count);
end
else
internalerror(2020041214);
end
else
begin
lastsec:=tai_section(hp);
{ also reset the last encountered symbol }
lastsym:=nil;
end;
if assigned(tmplist) then
begin
list.insertListBefore(hp,tmplist);
tmplist.free;
tmplist:=nil;
end;
end;
ait_symbol:
begin
if tai_symbol(hp).sym.typ=AT_FUNCTION then
lastsym:=tai_symbol(hp);
end;
ait_instruction:
if assigned(sehlist) then
inc(instrcount);
ait_const:
if assigned(sehlist) then
inc(datacount,tai_const(hp).size);
ait_seh_directive:
begin
if not assigned(sehlist) and (seh.kind<>ash_proc) then
internalerror(2020041208);
{ most seh directives are removed }
deleteai:=true;
case seh.kind of
ash_proc:
begin
if not assigned(lastsec) then
internalerror(2020041203);
datacount:=0;
instrcount:=0;
handlerflags:=0;
handlername:='';
sehlist:=tasmlist.create;
inprologue:=true;
end;
ash_endproc:
begin
if not assigned(sehlist) then
internalerror(2020041501);
if assigned(tmplist) then
internalerror(2020041302);
if not assigned(lastsym) then
internalerror(2020041303);
if inprologue then
cgmessage(asmw_e_missing_endprologue);
{ only generate xdata and pdata if we have any
prologue or a handler is set }
unwinddata:=convert_unwinddata(sehlist);
if unwinddata.size>0 then
begin
writebyte($E4);
{ fill up with NOPs }
while unwinddata.size mod 4<>0 do
writebyte($E3);
end;
if (handlerflags<>0) or (unwinddata.size<>0) then
begin
{ note: we can pass Nil here, because in case of a LLVM
backend this whole code shouldn't be required
anyway }
xdatasym:=current_asmdata.DefineAsmSymbol('xdata_'+lastsym.sym.name,AB_LOCAL,AT_DATA,nil);
tmplist:=tasmlist.create;
new_section(tmplist,sec_pdata,lastsec.name^,0);
tmplist.concat(tai_const.Create_rva_sym(lastsym.sym));
tmplist.concat(tai_const.Create_rva_sym(xdatasym));
new_section(tmplist,sec_rodata,xdatasym.name,sizeof(int32));
tmplist.concat(tai_symbol.Create(xdatasym,0));
tmplist.concat(tai_comment.Create(strpnew('instr: '+tostr(instrcount)+', data: '+tostr(datacount)+', unwind: '+tostr(unwinddata.size))));
{$ifdef EXTDEBUG}
comment(V_Debug,'got section: '+lastsec.name^);
comment(V_Debug,'got instructions: '+tostr(instrcount));
comment(V_Debug,'got data: '+tostr(datacount));
comment(V_Debug,'got unwinddata: '+tostr(unwinddata.size));
{$endif EXTDEBUG}
if datacount mod 4<>0 then
cgmessage(asmw_e_seh_invalid_data_size);
totalcount:=datacount div 4+instrcount;
{ splitting to multiple pdata/xdata sections is not yet
supported, so 1 MB is our limit for now }
if totalcount>(1 shl 18) then
comment(V_Error,'Function is larger than 1 MB which is not supported for SEH currently');
unwindrec:=min(totalcount,(1 shl 18)-1);
if handlerflags<>0 then
unwindrec:=unwindrec or (1 shl 20);
{ currently we only have one epilog, so E needs to be
set to 1 and epilog scope index needs to be 0, no
matter if we require the extension for the unwinddata
or not }
unwindrec:=unwindrec or (1 shl 21);
if unwinddata.size div 4<=31 then
unwindrec:=unwindrec or ((unwinddata.size div 4) shl 27);
{ exception record headers }
tmplist.concat(tai_const.Create_32bit(longint(unwindrec)));
if cs_asm_source in init_settings.globalswitches then
tmplist.concat(tai_comment.create(strpnew(hexstr(unwindrec,8))));
if unwinddata.size div 4>31 then
begin
{ once we're able to split a .pdata entry this can be
removed as well }
if unwinddata.size div 4>255 then
comment(V_Error,'Too many unwind codes for SEH');
unwindrec:=(unwinddata.size div 4) shl 16;
tmplist.concat(tai_const.create_32bit(longint(unwindrec)));
if cs_asm_source in init_settings.globalswitches then
tmplist.concat(tai_comment.create(strpnew(hexstr(unwindrec,8))));
end;
{ unwind codes }
unwinddata.seek(0);
while unwinddata.pos<unwinddata.size do
begin
unwinddata.read(unwindrec,sizeof(longword));
tmplist.concat(tai_const.Create_32bit(longint(unwindrec)));
if cs_asm_source in init_settings.globalswitches then
tmplist.concat(tai_comment.create(strpnew(hexstr(unwindrec,8))));
end;
if handlerflags<>0 then
begin
tmplist.concat(tai_const.Create_rva_sym(current_asmdata.RefAsmSymbol(handlername,AT_FUNCTION,false)));
if length(handlerdata)>0 then
begin
tmplist.concat(handlerdatacount);
for handlerdataidx:=0 to high(handlerdata) do
tmplist.concat(handlerdata[handlerdataidx]);
end;
end;
handlerdata:=nil;
end;
unwinddata.free;
sehlist.free;
sehlist:=nil;
end;
ash_endprologue:
inprologue:=false;
ash_handler:
begin
handlername:=seh.data.name^;
handlerflags:=seh.data.flags;
end;
ash_handlerdata:
begin
if handlername='' then
cgmessage(asmw_e_handlerdata_no_handler);
hpdata:=tai(hp.next);
if not assigned(hpdata) or (hpdata.typ<>ait_const) or (tai_const(hpdata).consttype<>aitconst_32bit) then
internalerror(2020041215);
handlerdatacount:=hpdata;
setlength(handlerdata,tai_const(hpdata).value*4);
handlerdataidx:=0;
hpnext:=tai(hpdata.next);
list.remove(hpdata);
hpdata:=hpnext;
while (handlerdataidx<length(handlerdata)) and assigned(hpdata) do
begin
if (hpdata.typ<>ait_const) or not (tai_const(hpdata).consttype in [aitconst_32bit,aitconst_rva_symbol]) then
internalerror(2020041212);
handlerdata[handlerdataidx]:=hpdata;
inc(handlerdataidx);
hpnext:=tai(hpdata.next);
list.remove(hpdata);
hpdata:=hpnext;
end;
if handlerdataidx<length(handlerdata) then
internalerror(2020041213);
end;
ash_stackalloc,
ash_addfp,
ash_setfp,
ash_nop,
ash_savefplr,
ash_savefplr_x,
ash_savereg,
ash_savereg_x,
ash_saveregp,
ash_saveregp_x,
ash_savefreg,
ash_savefreg_x,
ash_savefregp,
ash_savefregp_x:
begin
if not assigned(sehlist) then
internalerror(2020041504);
if not inprologue then
internalerror(2020041505);
hpdata:=hp;
hp:=tai(hp.previous);
list.Remove(hpdata);
sehlist.concat(hpdata);
{ don't delete this }
deleteai:=false;
end;
else
internalerror(2020041206);
end;
end;
else
{ ignore }
;
end;
if deleteai then
begin
hpnext:=tai(hp.next);
list.remove(hp);
hp.free;
hp:=hpnext;
end
else
hp:=tai(hp.next);
end;
if assigned(sehlist) then
internalerror(2020041205);
if assigned(tmplist) then
begin
list.concatlist(tmplist);
tmplist.free;
end;
end;
function TAArch64ClangGASAssembler.sectionflags(secflags:TSectionFlags):string;
begin
Result:=inherited sectionflags(secflags);
if (target_info.system=system_aarch64_win64) then
begin
{ we require an explicit "r" if write is not allowed }
if not (SF_W in secflags) then
result:=result+'r';
end;
end;
procedure TAArch64ClangGASAssembler.WriteAsmList;
begin
{ clang does not support all the directives we need, so we need to
manually transform them to pdata/xdata records }
if target_info.system=system_aarch64_win64 then
begin
TransformSEHDirectives(current_asmdata.AsmLists[al_pure_assembler]);
TransformSEHDirectives(current_asmdata.AsmLists[al_procedures]);
end;
inherited WriteAsmList;
end;
{****************************************************************************}
{ Helper routines for Instruction Writer }
{****************************************************************************}
function getreferencestring(asminfo: pasminfo; var ref : treference) : string;
const
darwin_addrpage2str: array[addr_page..addr_gotpageoffset] of string[11] =
('@PAGE','@PAGEOFF','@GOTPAGE','@GOTPAGEOFF');
linux_addrpage2str: array[addr_page..addr_gotpageoffset] of string[10] =
('',':lo12:',':got:',':got_lo12:');
begin
if ref.base=NR_NO then
begin
case ref.refaddr of
addr_gotpage,
addr_page,
addr_gotpageoffset,
addr_pageoffset:
begin
if not assigned(ref.symbol) or
(ref.base<>NR_NO) or
(ref.index<>NR_NO) or
(ref.shiftmode<>SM_None) or
(ref.offset<>0) then
internalerror(2014121501);
if target_info.system in systems_darwin then
result:=ref.symbol.name+darwin_addrpage2str[ref.refaddr]
else
result:=linux_addrpage2str[ref.refaddr]+ref.symbol.name
end;
addr_pic,
{ for locals replaced by temp symbols on LLVM }
addr_no:
result:=ref.symbol.name;
else
internalerror(2015022302);
end
end
else
begin
result:='['+gas_regname(ref.base);
if ref.addressmode=AM_POSTINDEXED then
result:=result+']';
if ref.index<>NR_NO then
begin
if (ref.offset<>0) or
assigned(ref.symbol) then
internalerror(2014121504);
result:=result+', '+gas_regname(ref.index);
case ref.shiftmode of
SM_None: ;
SM_LSL,
SM_UXTW, SM_UXTX, SM_SXTW, SM_SXTX:
begin
result:=result+', '+gas_shiftmode2str[ref.shiftmode];
if (ref.shiftmode=SM_LSL) or
(ref.shiftimm<>0) then
result:=result+' #'+tostr(ref.shiftimm);
end
else
internalerror(2014121505);
end;
end
else
begin
if assigned(ref.symbol) then
begin
case ref.refaddr of
addr_gotpageoffset,
addr_pageoffset:
begin
if target_info.system in systems_darwin then
result:=result+', '+ref.symbol.name+darwin_addrpage2str[ref.refaddr]
else
result:=result+', '+linux_addrpage2str[ref.refaddr]+ref.symbol.name
end
else
{ todo: not yet generated/don't know syntax }
internalerror(2014121506);
end;
end
else
begin
if ref.refaddr<>addr_no then
internalerror(2014121502);
if (ref.offset<>0) then
result:=result+', #'+tostr(ref.offset);
end;
end;
case ref.addressmode of
AM_OFFSET:
result:=result+']';
AM_PREINDEXED:
result:=result+']!';
else
;
end;
end;
end;
function getopstr(asminfo: pasminfo; hp: taicpu; opnr: longint; const o: toper): string;
var
i: longint;
reg: tregister;
begin
case o.typ of
top_reg:
getopstr:=gas_regname(o.reg);
top_shifterop:
begin
getopstr:=gas_shiftmode2str[o.shifterop^.shiftmode];
if o.shifterop^.shiftimm<>0 then
getopstr:=getopstr+' #'+tostr(o.shifterop^.shiftimm)
end;
top_const:
if o.val>=0 then
getopstr:='#'+tostr(o.val)
else
getopstr:='#0x'+hexStr(o.val,16);
top_conditioncode:
getopstr:=cond2str[o.cc];
top_ref:
if is_calljmp(hp.opcode) then
begin
if o.ref^.refaddr<>addr_full then
internalerror(2014122220);
if not assigned(o.ref^.symbol) or
assigned(o.ref^.relsymbol) or
(o.ref^.base<>NR_NO) or
(o.ref^.index<>NR_NO) or
(o.ref^.offset<>0) then
internalerror(2014122221);
getopstr:=o.ref^.symbol.name;
end
else
getopstr:=getreferencestring(asminfo,o.ref^);
top_realconst:
begin
str(o.val_real,Result);
Result:='#'+Result;
end;
top_regset:
begin
reg:=o.basereg;
result:='{'+gas_regname(reg);
for i:=1 to o.nregs-1 do
begin
setsupreg(reg,succ(getsupreg(reg)) mod 32);
result:=result+', '+gas_regname(reg);
end;
result:=result+'}';
if o.regsetindex<>255 then
result:=result+'['+tostr(o.regsetindex)+']'
end;
top_indexedreg:
begin
result:=gas_regname(o.indexedreg)+'['+tostr(o.regindex)+']';
end;
else
internalerror(2014121507);
end;
end;
procedure TAArch64InstrWriter.WriteInstruction(hp : tai);
var
op: TAsmOp;
s: string;
i: byte;
sep: string[3];
begin
op:=taicpu(hp).opcode;
s:=#9+gas_op2str[op]+oppostfix2str[taicpu(hp).oppostfix];
if taicpu(hp).condition<>C_NONE then
s:=s+'.'+cond2str[taicpu(hp).condition];
if taicpu(hp).ops<>0 then
begin
sep:=#9;
for i:=0 to taicpu(hp).ops-1 do
begin
// debug code
// writeln(s);
// writeln(taicpu(hp).fileinfo.line);
s:=s+sep+getopstr(owner.asminfo,taicpu(hp),i,taicpu(hp).oper[i]^);
sep:=',';
end;
end;
owner.writer.AsmWriteLn(s);
end;
const
as_aarch64_gas_info : tasminfo =
(
id : as_gas;
idtxt : 'AS';
asmbin : 'as';
asmcmd : '-o $OBJ $MARCHOPT $EXTRAOPT $ASM';
supported_targets : [system_aarch64_freebsd,system_aarch64_linux,system_aarch64_android,system_aarch64_embedded];
flags : [af_needar,af_smartlink_sections];
labelprefix : '.L';
labelmaxlen : -1;
comment : '// ';
dollarsign: '$';
);
as_aarch64_clang_darwin_info : tasminfo =
(
id : as_clang_asdarwin;
idtxt : 'CLANG';
asmbin : 'clang';
asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $MARCHOPT $EXTRAOPT -x assembler $ASM';
supported_targets : [system_aarch64_ios,system_aarch64_darwin,system_aarch64_iphonesim];
flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm,af_supports_hlcfi];
labelprefix : 'L';
labelmaxlen : -1;
comment : '# ';
dollarsign: '$';
);
as_aarch64_clang_gas_info : tasminfo =
(
id : as_clang_gas;
idtxt : 'AS-CLANG';
asmbin : 'clang';
asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $MARCHOPT $EXTRAOPT -x assembler $ASM';
supported_targets : [system_aarch64_freebsd,system_aarch64_linux,system_aarch64_android,system_aarch64_embedded,system_aarch64_win64];
flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm,af_supports_hlcfi];
labelprefix : '.L';
labelmaxlen : -1;
comment : '// ';
dollarsign: '$';
);
as_aarch64_win64_gas_info : tasminfo =
(
id : as_win64_gas;
idtxt : 'WIN64-AS';
asmbin : 'as';
asmcmd : '-o $OBJ $MARCHOPT $EXTRAOPT $ASM';
supported_targets : [system_aarch64_win64];
flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm,af_supports_hlcfi];
labelprefix : '.L';
labelmaxlen : -1;
comment : '// ';
dollarsign: '$';
);
begin
RegisterAssembler(as_aarch64_gas_info,TAArch64Assembler);
RegisterAssembler(as_aarch64_clang_darwin_info,TAArch64AppleAssembler);
RegisterAssembler(as_aarch64_clang_gas_info,TAArch64ClangGASAssembler);
RegisterAssembler(as_aarch64_win64_gas_info,TAArch64ClangGASAssembler);
end.