{ Copyright (c) 1998-2013 by the Free Pascal team This unit implements the generic part of the LLVM IR writer 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 agllvm; {$i fpcdefs.inc} interface uses globtype,globals,systems, aasmbase,aasmtai,aasmdata, assemble, aasmllvm; type TLLVMInstrWriter = class; TLLVMModuleInlineAssemblyDecorator = class(IExternalAssemblerOutputFileDecorator) function LineFilter(const s: AnsiString): AnsiString; function LinePrefix: AnsiString; function LinePostfix: AnsiString; function LineEnding(const deflineending: ShortString): ShortString; end; TLLVMFunctionInlineAssemblyDecorator = class(IExternalAssemblerOutputFileDecorator) function LineFilter(const s: AnsiString): AnsiString; function LinePrefix: AnsiString; function LinePostfix: AnsiString; function LineEnding(const deflineending: ShortString): ShortString; end; TLLVMAssember=class(texternalassembler) protected ffuncinlasmdecorator: TLLVMFunctionInlineAssemblyDecorator; fdecllevel: longint; procedure WriteExtraHeader;virtual; procedure WriteExtraFooter;virtual; procedure WriteInstruction(hp: tai); procedure WriteLlvmInstruction(hp: tai); procedure WriteDirectiveName(dir: TAsmDirective); virtual; procedure WriteRealConst(hp: tai_realconst; do_line: boolean); procedure WriteOrdConst(hp: tai_const); procedure WriteTai(const replaceforbidden: boolean; const do_line: boolean; var InlineLevel: cardinal; var asmblock: boolean; var hp: tai); public constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override; function MakeCmdLine: TCmdStr; override; procedure WriteTree(p:TAsmList);override; procedure WriteAsmList;override; procedure WriteFunctionInlineAsmList(list: tasmlist); destructor destroy; override; protected InstrWriter: TLLVMInstrWriter; end; {# This is the base class for writing instructions. The WriteInstruction() method must be overridden to write a single instruction to the assembler file. } TLLVMInstrWriter = class constructor create(_owner: TLLVMAssember); procedure WriteInstruction(hp : tai); protected owner: TLLVMAssember; fstr: TSymStr; function getopcodestr(hp: taillvm): TSymStr; function getopstr(const o:toper; refwithalign: boolean) : TSymStr; procedure WriteAsmRegisterAllocationClobbers(list: tasmlist); end; implementation uses SysUtils, cutils,cclasses,cfileutl, fmodule,verbose, objcasm, aasmcnst,symconst,symdef,symtable, llvmbase,itllvm,llvmdef, cgbase,cgutils,cpubase,llvminfo; const line_length = 70; type {$ifdef cpuextended} t80bitarray = array[0..9] of byte; {$endif cpuextended} t64bitarray = array[0..7] of byte; t32bitarray = array[0..3] of byte; {****************************************************************************} { Support routines } {****************************************************************************} function single2str(d : single) : string; var hs : string; begin str(d,hs); { replace space with + } if hs[1]=' ' then hs[1]:='+'; single2str:=hs end; function double2str(d : double) : string; var hs : string; begin str(d,hs); { replace space with + } if hs[1]=' ' then hs[1]:='+'; double2str:=hs end; function extended2str(e : extended) : string; var hs : string; begin str(e,hs); { replace space with + } if hs[1]=' ' then hs[1]:='+'; extended2str:=hs end; {****************************************************************************} { Decorator for module-level inline assembly } {****************************************************************************} function TLLVMModuleInlineAssemblyDecorator.LineFilter(const s: AnsiString): AnsiString; var i: longint; begin result:=''; for i:=1 to length(s) do begin case s[i] of #0..#31, #127..#255, '"','\': result:=result+ '\'+ chr((ord(s[i]) shr 4)+ord('0'))+ chr((ord(s[i]) and $f)+ord('0')); else result:=result+s[i]; end; end; end; function TLLVMModuleInlineAssemblyDecorator.LinePrefix: AnsiString; begin result:='module asm "'; end; function TLLVMModuleInlineAssemblyDecorator.LinePostfix: AnsiString; begin result:='"'; end; function TLLVMModuleInlineAssemblyDecorator.LineEnding(const deflineending: ShortString): ShortString; begin result:=deflineending end; {****************************************************************************} { Decorator for function-level inline assembly } {****************************************************************************} function TLLVMFunctionInlineAssemblyDecorator.LineFilter(const s: AnsiString): AnsiString; var i: longint; begin result:=''; for i:=1 to length(s) do begin case s[i] of { escape dollars } '$': result:=result+'$$'; { ` is used as placeholder for a single dollar (reference to argument to the inline assembly) } '`': result:=result+'$'; #0..#31, #127..#255, '"','\': result:=result+ '\'+ chr((ord(s[i]) shr 4)+ord('0'))+ chr((ord(s[i]) and $f)+ord('0')); else result:=result+s[i]; end; end; end; function TLLVMFunctionInlineAssemblyDecorator.LinePrefix: AnsiString; begin result:=''; end; function TLLVMFunctionInlineAssemblyDecorator.LinePostfix: AnsiString; begin result:=''; end; function TLLVMFunctionInlineAssemblyDecorator.LineEnding(const deflineending: ShortString): ShortString; begin result:='\0A'; end; {****************************************************************************} { LLVM Instruction writer } {****************************************************************************} function getregisterstring(reg: tregister): ansistring; begin if getregtype(reg)=R_TEMPREGISTER then result:='%tmp.' else result:='%reg.'+tostr(byte(getregtype(reg)))+'_'; result:=result+tostr(getsupreg(reg)); end; function getreferencealignstring(var ref: treference) : ansistring; begin result:=', align '+tostr(ref.alignment); end; function getreferencestring(var ref : treference; withalign: boolean) : ansistring; begin result:=''; if assigned(ref.relsymbol) or (assigned(ref.symbol) and (ref.base<>NR_NO)) or (ref.index<>NR_NO) or (ref.offset<>0) then begin result:=' **(error ref: '; if assigned(ref.symbol) then result:=result+'sym='+ref.symbol.name+', '; if assigned(ref.relsymbol) then result:=result+'sym='+ref.relsymbol.name+', '; if ref.base=NR_NO then result:=result+'base=NR_NO, '; if ref.index<>NR_NO then result:=result+'index<>NR_NO, '; if ref.offset<>0 then result:=result+'offset='+tostr(ref.offset); result:=result+')**'; internalerror(2013060225); end; if ref.base<>NR_NO then result:=result+getregisterstring(ref.base) else if assigned(ref.symbol) then result:=result+LlvmAsmSymName(ref.symbol) else result:=result+'null'; if withalign then result:=result+getreferencealignstring(ref); end; function getparas(const paras: tfplist): ansistring; var i: longint; para: pllvmcallpara; begin result:='('; for i:=0 to paras.count-1 do begin if i<>0 then result:=result+', '; para:=pllvmcallpara(paras[i]); result:=result+llvmencodetypename(para^.def); if para^.valueext<>lve_none then result:=result+llvmvalueextension2str[para^.valueext]; if para^.byval then result:=result+' byval'; if para^.sret then result:=result+' sret'; case para^.loc of LOC_REGISTER, LOC_FPUREGISTER, LOC_MMREGISTER: result:=result+' '+getregisterstring(para^.reg); LOC_CONSTANT: result:=result+' '+tostr(int64(para^.value)); { empty records } LOC_VOID: result:=result+' undef'; else internalerror(2014010801); end; end; result:=result+')'; end; function llvmdoubletostr(const d: double): TSymStr; type tdoubleval = record case byte of 1: (d: double); 2: (i: int64); end; begin { "When using the hexadecimal form, constants of types half, float, and double are represented using the 16-digit form shown above (which matches the IEEE754 representation for double)" And always in big endian form (sign bit leftmost) } result:='0x'+hexstr(tdoubleval(d).i,16); end; {$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)} function llvmextendedtostr(const e: extended): TSymStr; var extendedval: record case byte of 1: (e: extended); 2: (r: packed record {$ifdef FPC_LITTLE_ENDIAN} l: int64; h: word; {$else FPC_LITTLE_ENDIAN} h: int64; l: word; {$endif FPC_LITTLE_ENDIAN} end; ); end; begin extendedval.e:=e; { hex format is always big endian in llvm } result:='0xK'+hexstr(extendedval.r.h,sizeof(extendedval.r.h)*2)+ hexstr(extendedval.r.l,sizeof(extendedval.r.l)*2); end; {$endif cpuextended} function TLLVMInstrWriter.getopstr(const o:toper; refwithalign: boolean) : TSymStr; var hs : ansistring; hp: tai; tmpinline: cardinal; tmpasmblock: boolean; begin case o.typ of top_reg: getopstr:=getregisterstring(o.reg); top_const: getopstr:=tostr(int64(o.val)); top_ref: if o.ref^.refaddr=addr_full then begin getopstr:=''; if assigned(o.ref^.symbol) then getopstr:=LlvmAsmSymName(o.ref^.symbol) else getopstr:='null'; if o.ref^.offset<>0 then internalerror(2013060223); end else getopstr:=getreferencestring(o.ref^,refwithalign); top_def: begin getopstr:=llvmencodetypename(o.def); end; top_cond: begin getopstr:=llvm_cond2str[o.cond]; end; top_fpcond: begin getopstr:=llvm_fpcond2str[o.fpcond]; end; top_single, top_double: begin { "When using the hexadecimal form, constants of types half, float, and double are represented using the 16-digit form shown above (which matches the IEEE754 representation for double)" And always in big endian form (sign bit leftmost) } if o.typ=top_double then result:=llvmdoubletostr(o.dval) else result:=llvmdoubletostr(o.sval) end; top_para: begin result:=getparas(o.paras); end; top_tai: begin if assigned(o.ai) then begin tmpinline:=1; tmpasmblock:=false; hp:=o.ai; owner.writer.AsmWrite(fstr); fstr:=''; owner.WriteTai(false,false,tmpinline,tmpasmblock,hp); end; result:=''; end; {$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)} top_extended80: begin result:=llvmextendedtostr(o.eval); end; {$endif cpuextended} top_undef: result:='undef' else internalerror(2013060227); end; end; procedure TLLVMInstrWriter.WriteAsmRegisterAllocationClobbers(list: tasmlist); var hp: tai; begin hp:=tai(list.first); while assigned(hp) do begin if (hp.typ=ait_regalloc) and (tai_regalloc(hp).ratype=ra_alloc) then begin owner.writer.AsmWrite(',~{'); owner.writer.AsmWrite(std_regname(tai_regalloc(hp).reg)); owner.writer.AsmWrite('}'); end; hp:=tai(hp.next); end; end; procedure TLLVMInstrWriter.WriteInstruction(hp: tai); var op: tllvmop; tmpstr, sep: TSymStr; i, opstart: longint; nested: boolean; opdone, done: boolean; begin op:=taillvm(hp).llvmopcode; { we write everything immediately rather than adding it into a string, because operands may contain other tai that will also write things out (and their output must come after everything that was processed in this instruction, such as its opcode or previous operands) } if owner.fdecllevel=0 then owner.writer.AsmWrite(#9); sep:=' '; opdone:=false; done:=false; opstart:=0; nested:=false; case op of la_type: begin owner.writer.AsmWrite(llvmtypeidentifier(taillvm(hp).oper[0]^.def)); owner.writer.AsmWrite(' = type '); owner.writer.AsmWrite(llvmencodetypedecl(taillvm(hp).oper[0]^.def)); done:=true; end; la_asmblock: begin owner.writer.AsmWrite('call void asm sideeffect "'); owner.WriteFunctionInlineAsmList(taillvm(hp).oper[0]^.asmlist); owner.writer.AsmWrite('","'); { we pass all accessed local variables as in/out address parameters, since we don't analyze the assembly code to determine what exactly happens to them; this is also compatible with the regular code generators, which always place local place local variables accessed from assembly code in memory } for i:=0 to taillvm(hp).oper[1]^.paras.Count-1 do begin owner.writer.AsmWrite('=*m,'); end; owner.writer.AsmWrite('~{memory},~{fpsr},~{flags}'); WriteAsmRegisterAllocationClobbers(taillvm(hp).oper[0]^.asmlist); owner.writer.AsmWrite('"'); owner.writer.AsmWrite(getparas(taillvm(hp).oper[1]^.paras)); done:=true; end; la_load, la_getelementptr: begin if (taillvm(hp).oper[0]^.typ<>top_reg) or (taillvm(hp).oper[0]^.reg<>NR_NO) then owner.writer.AsmWrite(getopstr(taillvm(hp).oper[0]^,false)+' = ') else nested:=true; opstart:=1; if llvmflag_load_getelptr_type in llvmversion_properties[current_settings.llvmversion] then begin owner.writer.AsmWrite(getopcodestr(taillvm(hp))); opdone:=true; if nested then owner.writer.AsmWrite(' (') else owner.writer.AsmWrite(' '); { can't just dereference the type, because it may be an implicit pointer type such as a class -> resort to string manipulation... Not very clean :( } tmpstr:=llvmencodetypename(taillvm(hp).spilling_get_reg_type(0)); if op=la_getelementptr then begin if tmpstr[length(tmpstr)]<>'*' then begin writeln(tmpstr); internalerror(2016071101); end else setlength(tmpstr,length(tmpstr)-1); end; owner.writer.AsmWrite(tmpstr); owner.writer.AsmWrite(','); end end; la_ret, la_br, la_switch, la_indirectbr, la_invoke, la_resume, la_unreachable, la_store, la_fence, la_cmpxchg, la_atomicrmw, la_catch, la_filter: begin { instructions that never have a result } end; la_call: begin if taillvm(hp).oper[1]^.reg<>NR_NO then owner.writer.AsmWrite(getregisterstring(taillvm(hp).oper[1]^.reg)+' = '); opstart:=2; if llvmflag_call_no_ptr in llvmversion_properties[current_settings.llvmversion] then begin owner.writer.AsmWrite(getopcodestr(taillvm(hp))); opdone:=true; tmpstr:=llvmencodetypename(taillvm(hp).oper[2]^.def); if tmpstr[length(tmpstr)]<>'*' then begin writeln(tmpstr); internalerror(2016071102); end else setlength(tmpstr,length(tmpstr)-1); owner.writer.AsmWrite(tmpstr); opstart:=3; end; end; la_blockaddress: begin { nested -> no type } if owner.fdecllevel = 0 then begin owner.writer.AsmWrite(getopstr(taillvm(hp).oper[0]^,false)); owner.writer.AsmWrite(' '); end; owner.writer.AsmWrite('blockaddress('); owner.writer.AsmWrite(getopstr(taillvm(hp).oper[1]^,false)); { getopstr would add a "label" qualifier, which blockaddress does not want } owner.writer.AsmWrite(',%'); with taillvm(hp).oper[2]^ do begin if (typ<>top_ref) or (ref^.refaddr<>addr_full) then internalerror(2016112001); owner.writer.AsmWrite(ref^.symbol.name); end; nested:=true; done:=true; end; la_alloca: begin owner.writer.AsmWrite(getreferencestring(taillvm(hp).oper[0]^.ref^,false)+' = '); sep:=' '; opstart:=1; end; la_trunc, la_zext, la_sext, la_fptrunc, la_fpext, la_fptoui, la_fptosi, la_uitofp, la_sitofp, la_ptrtoint, la_inttoptr, la_bitcast: begin { destination can be empty in case of nested constructs, or data initialisers } if (taillvm(hp).oper[0]^.typ<>top_reg) or (taillvm(hp).oper[0]^.reg<>NR_NO) then owner.writer.AsmWrite(getopstr(taillvm(hp).oper[0]^,false)+' = ') else nested:=true; owner.writer.AsmWrite(getopcodestr(taillvm(hp))); if not nested then owner.writer.AsmWrite(' ') else owner.writer.AsmWrite(' ('); owner.writer.AsmWrite(getopstr(taillvm(hp).oper[1]^,false)); { if there's a tai operand, its def is used instead of an explicit def operand } if taillvm(hp).ops=4 then begin owner.writer.AsmWrite(' '); owner.writer.AsmWrite(getopstr(taillvm(hp).oper[2]^,false)); opstart:=3; end else opstart:=2; owner.writer.AsmWrite(' to '); owner.writer.AsmWrite(getopstr(taillvm(hp).oper[opstart]^,false)); done:=true; end else begin if (taillvm(hp).oper[0]^.typ<>top_reg) or (taillvm(hp).oper[0]^.reg<>NR_NO) then begin owner.writer.AsmWrite(getopstr(taillvm(hp).oper[0]^,true)+' = '); end else nested:=true; sep:=' '; opstart:=1 end; end; { process operands } if not done then begin if not opdone then begin owner.writer.AsmWrite(getopcodestr(taillvm(hp))); if nested then owner.writer.AsmWrite(' ('); end; if taillvm(hp).ops<>0 then begin for i:=opstart to taillvm(hp).ops-1 do begin owner.writer.AsmWrite(sep); owner.writer.AsmWrite(getopstr(taillvm(hp).oper[i]^,op in [la_load,la_store])); if (taillvm(hp).oper[i]^.typ in [top_def,top_cond,top_fpcond]) or (op in [la_call,la_landingpad,la_catch,la_filter]) then sep :=' ' else sep:=', '; end; end; end; if op=la_alloca then owner.writer.AsmWrite(getreferencealignstring(taillvm(hp).oper[0]^.ref^)); if nested then owner.writer.AsmWrite(')') else if owner.fdecllevel=0 then owner.writer.AsmLn; end; function TLLVMInstrWriter.getopcodestr(hp: taillvm): TSymStr; begin result:=llvm_op2str[hp.llvmopcode]; case hp.llvmopcode of la_load: begin if vol_read in hp.oper[2]^.ref^.volatility then result:=result+' volatile'; end; la_store: begin if vol_write in hp.oper[3]^.ref^.volatility then result:=result+' volatile'; end; else ; end; end; {****************************************************************************} { LLVM Assembler writer } {****************************************************************************} destructor TLLVMAssember.Destroy; begin InstrWriter.free; ffuncinlasmdecorator.free; inherited destroy; end; function TLLVMAssember.MakeCmdLine: TCmdStr; var optstr: TCmdStr; begin result := inherited MakeCmdLine; { standard optimization flags for llc -- todo: this needs to be split into a call to opt and one to llc } if cs_opt_level3 in current_settings.optimizerswitches then optstr:='-O3' else if cs_opt_level2 in current_settings.optimizerswitches then optstr:='-O2' else if cs_opt_level1 in current_settings.optimizerswitches then optstr:='-O1' else optstr:='-O0'; { stack frame elimination } if not(cs_opt_stackframe in current_settings.optimizerswitches) then optstr:=optstr+' -disable-fp-elim'; { fast math } if cs_opt_fastmath in current_settings.optimizerswitches then optstr:=optstr+' -enable-unsafe-fp-math -enable-fp-mad -fp-contract=fast'; { smart linking } if cs_create_smart in current_settings.moduleswitches then optstr:=optstr+' -data-sections -function-sections'; { pic } if cs_create_pic in current_settings.moduleswitches then optstr:=optstr+' -relocation-model=pic' else if not(target_info.system in systems_darwin) then optstr:=optstr+' -relocation-model=static' else optstr:=optstr+' -relocation-model=dynamic-no-pic'; { our stack alignment is non-standard on some targets. The following parameter is however ignored on some targets by llvm, so it may not be enough } optstr:=optstr+' -stack-alignment='+tostr(target_info.stackalign*8); { force object output instead of textual assembler code } optstr:=optstr+' -filetype=obj'; replace(result,'$OPT',optstr); end; procedure TLLVMAssember.WriteTree(p:TAsmList); var hp : tai; InlineLevel : cardinal; asmblock: boolean; do_line : boolean; replaceforbidden: boolean; begin if not assigned(p) then exit; replaceforbidden:=asminfo^.dollarsign<>'$'; InlineLevel:=0; asmblock:=false; { lineinfo is only needed for al_procedures (PFV) } do_line:=(cs_asm_source in current_settings.globalswitches) or ((cs_lineinfo in current_settings.moduleswitches) and (p=current_asmdata.asmlists[al_procedures])); hp:=tai(p.first); while assigned(hp) do begin prefetch(pointer(hp.next)^); if not(hp.typ in SkipLineInfo) then begin current_filepos:=tailineinfo(hp).fileinfo; { no line info for inlined code } if do_line and (inlinelevel=0) then WriteSourceLine(hp as tailineinfo); end; WriteTai(replaceforbidden, do_line, InlineLevel, asmblock, hp); hp:=tai(hp.next); end; end; procedure TLLVMAssember.WriteExtraHeader; begin writer.AsmWrite('target datalayout = "'); writer.AsmWrite(target_info.llvmdatalayout); writer.AsmWriteln('"'); writer.AsmWrite('target triple = "'); writer.AsmWrite(llvm_target_name); writer.AsmWriteln('"'); end; procedure TLLVMAssember.WriteExtraFooter; begin end; procedure TLLVMAssember.WriteInstruction(hp: tai); begin end; procedure TLLVMAssember.WriteLlvmInstruction(hp: tai); begin InstrWriter.WriteInstruction(hp); end; procedure TLLVMAssember.WriteRealConst(hp: tai_realconst; do_line: boolean); begin if fdecllevel=0 then begin case tai_realconst(hp).realtyp of aitrealconst_s32bit: writer.AsmWriteLn(asminfo^.comment+'value: '+single2str(tai_realconst(hp).value.s32val)); aitrealconst_s64bit: writer.AsmWriteLn(asminfo^.comment+'value: '+double2str(tai_realconst(hp).value.s64val)); {$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)} { can't write full 80 bit floating point constants yet on non-x86 } aitrealconst_s80bit: writer.AsmWriteLn(asminfo^.comment+'value: '+extended2str(tai_realconst(hp).value.s80val)); {$endif cpuextended} aitrealconst_s64comp: writer.AsmWriteLn(asminfo^.comment+'value: '+extended2str(tai_realconst(hp).value.s64compval)); else internalerror(2014050604); end; internalerror(2016120202); end; case hp.realtyp of aitrealconst_s32bit: writer.AsmWrite(llvmdoubletostr(hp.value.s32val)); aitrealconst_s64bit: writer.AsmWriteln(llvmdoubletostr(hp.value.s64val)); {$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)} aitrealconst_s80bit: writer.AsmWrite(llvmextendedtostr(hp.value.s80val)); {$endif defined(cpuextended)} aitrealconst_s64comp: { handled as int64 most of the time in llvm } writer.AsmWrite(tostr(round(hp.value.s64compval))); else internalerror(2014062401); end; end; procedure TLLVMAssember.WriteOrdConst(hp: tai_const); var consttyp: taiconst_type; begin if fdecllevel=0 then internalerror(2016120203); consttyp:=hp.consttype; case consttyp of aitconst_got, aitconst_gotoff_symbol, aitconst_uleb128bit, aitconst_sleb128bit, aitconst_rva_symbol, aitconst_secrel32_symbol, aitconst_darwin_dwarf_delta32, aitconst_darwin_dwarf_delta64, aitconst_half16bit, aitconst_gs: internalerror(2014052901); aitconst_128bit, aitconst_64bit, aitconst_32bit, aitconst_16bit, aitconst_8bit, aitconst_16bit_unaligned, aitconst_32bit_unaligned, aitconst_64bit_unaligned: begin if fdecllevel=0 then writer.AsmWrite(asminfo^.comment); { can't have compile-time differences between symbols; these are normally for PIC, but llvm takes care of that for us } if assigned(hp.endsym) then internalerror(2014052902); if assigned(hp.sym) then begin writer.AsmWrite(LlvmAsmSymName(hp.sym)); { can't have offsets } if hp.value<>0 then if fdecllevel<>0 then internalerror(2014052903) else writer.AsmWrite(' -- symbol offset: ' + tostr(hp.value)); end else if hp.value=0 then writer.AsmWrite('zeroinitializer') else writer.AsmWrite(tostr(hp.value)); { // activate in case of debugging IE 2016120203 if fdecllevel=0 then writer.AsmLn; } end; else internalerror(200704251); end; end; procedure TLLVMAssember.WriteTai(const replaceforbidden: boolean; const do_line: boolean; var InlineLevel: cardinal; var asmblock: boolean; var hp: tai); procedure WriteLinkageVibilityFlags(bind: TAsmSymBind); begin case bind of AB_EXTERNAL, AB_EXTERNAL_INDIRECT: writer.AsmWrite(' external'); AB_COMMON: writer.AsmWrite(' common'); AB_LOCAL: writer.AsmWrite(' internal'); AB_GLOBAL, AB_INDIRECT: ; AB_WEAK_EXTERNAL: writer.AsmWrite(' extern_weak'); AB_PRIVATE_EXTERN: begin if not(llvmflag_linker_private in llvmversion_properties[current_settings.llvmversion]) then writer.AsmWrite(' hidden') else writer.AsmWrite(' linker_private'); end else internalerror(2014020104); end; end; procedure WriteFunctionFlags(pd: tprocdef); begin if (pos('FPC_SETJMP',upper(pd.mangledname))<>0) or (pd.mangledname=(target_info.cprefix+'setjmp')) then writer.AsmWrite(' returns_twice'); if po_inline in pd.procoptions then writer.AsmWrite(' inlinehint'); if po_noinline in pd.procoptions then writer.AsmWrite(' noinline'); { ensure that functions that happen to have the same name as a standard C library function, but which are implemented in Pascal, are not considered to have the same semantics as the C function with the same name } if not(po_external in pd.procoptions) then writer.AsmWrite(' nobuiltin'); if po_noreturn in pd.procoptions then writer.AsmWrite(' noreturn'); end; procedure WriteTypedConstData(hp: tai_abstracttypedconst); var p: tai_abstracttypedconst; pval: tai; defstr: TSymStr; first, gotstring: boolean; begin defstr:=llvmencodetypename(hp.def); { write the struct, array or simple type } case hp.adetyp of tck_record: begin writer.AsmWrite(defstr); writer.AsmWrite(' <{'); first:=true; for p in tai_aggregatetypedconst(hp) do begin if not first then writer.AsmWrite(', ') else first:=false; WriteTypedConstData(p); end; writer.AsmWrite('}>'); end; tck_array: begin writer.AsmWrite(defstr); first:=true; gotstring:=false; for p in tai_aggregatetypedconst(hp) do begin if not first then writer.AsmWrite(',') else begin writer.AsmWrite(' '); if (tai_abstracttypedconst(p).adetyp=tck_simple) and (tai_simpletypedconst(p).val.typ=ait_string) then begin gotstring:=true; end else begin writer.AsmWrite('['); end; first:=false; end; { cannot concat strings and other things } if gotstring and ((tai_abstracttypedconst(p).adetyp<>tck_simple) or (tai_simpletypedconst(p).val.typ<>ait_string)) then internalerror(2014062701); WriteTypedConstData(p); end; if not gotstring then writer.AsmWrite(']'); end; tck_simple: begin pval:=tai_simpletypedconst(hp).val; if pval.typ<>ait_string then begin writer.AsmWrite(defstr); writer.AsmWrite(' '); end; WriteTai(replaceforbidden,do_line,InlineLevel,asmblock,pval); end; end; end; var hp2: tai; s: string; sstr: TSymStr; i: longint; ch: ansichar; begin case hp.typ of ait_comment : begin writer.AsmWrite(asminfo^.comment); writer.AsmWritePChar(tai_comment(hp).str); if fdecllevel<>0 then internalerror(2015090601); writer.AsmLn; end; ait_regalloc : begin if (cs_asm_regalloc in current_settings.globalswitches) then begin writer.AsmWrite(#9+asminfo^.comment+'Register '); repeat writer.AsmWrite(std_regname(Tai_regalloc(hp).reg)); if (hp.next=nil) or (tai(hp.next).typ<>ait_regalloc) or (tai_regalloc(hp.next).ratype<>tai_regalloc(hp).ratype) then break; hp:=tai(hp.next); writer.AsmWrite(','); until false; writer.AsmWrite(' '); writer.AsmWriteLn(regallocstr[tai_regalloc(hp).ratype]); end; end; ait_tempalloc : begin if (cs_asm_tempalloc in current_settings.globalswitches) then WriteTempalloc(tai_tempalloc(hp)); end; ait_align, ait_section : begin { ignore, specified as part of declarations -- don't write comment, because could appear in the middle of an aggregate constant definition } end; ait_datablock : begin writer.AsmWrite(asminfo^.comment); writer.AsmWriteln('datablock'); end; ait_const: begin WriteOrdConst(tai_const(hp)); end; ait_realconst : begin WriteRealConst(tai_realconst(hp), do_line); end; ait_string : begin if fdecllevel=0 then internalerror(2016120201); writer.AsmWrite('c"'); for i:=1 to tai_string(hp).len do begin ch:=tai_string(hp).str[i-1]; case ch of #0, {This can't be done by range, because a bug in FPC} #1..#31, #128..#255, '"', '\' : s:='\'+hexStr(ord(ch),2); else s:=ch; end; writer.AsmWrite(s); end; writer.AsmWrite('"'); end; ait_label : begin if not asmblock and (tai_label(hp).labsym.is_used) then begin if (tai_label(hp).labsym.bind=AB_PRIVATE_EXTERN) then begin { should be emitted as part of the variable/function def } internalerror(2013010703); end; if tai_label(hp).labsym.bind in [AB_GLOBAL, AB_PRIVATE_EXTERN] then begin { should be emitted as part of the variable/function def } //internalerror(2013010704); writer.AsmWriteln(asminfo^.comment+'global/privateextern label: '+tai_label(hp).labsym.name); end; if replaceforbidden then writer.AsmWrite(ReplaceForbiddenAsmSymbolChars(tai_label(hp).labsym.name)) else writer.AsmWrite(tai_label(hp).labsym.name); writer.AsmWriteLn(':'); end; end; ait_symbol : begin if fdecllevel=0 then writer.AsmWrite(asminfo^.comment); writer.AsmWriteln(LlvmAsmSymName(tai_symbol(hp).sym)); { todo } if tai_symbol(hp).has_value then internalerror(2014062402); end; ait_llvmdecl: begin if taillvmdecl(hp).def.typ=procdef then begin if not(ldf_definition in taillvmdecl(hp).flags) then begin writer.AsmWrite('declare'); writer.AsmWrite(llvmencodeproctype(tprocdef(taillvmdecl(hp).def), taillvmdecl(hp).namesym.name, lpd_decl)); WriteFunctionFlags(tprocdef(taillvmdecl(hp).def)); writer.AsmLn; end else begin writer.AsmWrite('define'); if ldf_weak in taillvmdecl(hp).flags then writer.AsmWrite(' weak'); WriteLinkageVibilityFlags(taillvmdecl(hp).namesym.bind); writer.AsmWrite(llvmencodeproctype(tprocdef(taillvmdecl(hp).def), '', lpd_def)); WriteFunctionFlags(tprocdef(taillvmdecl(hp).def)); writer.AsmWriteln(' {'); end; end else begin writer.AsmWrite(LlvmAsmSymName(taillvmdecl(hp).namesym)); writer.AsmWrite(' ='); if ldf_weak in taillvmdecl(hp).flags then writer.AsmWrite(' weak'); if ldf_appending in taillvmdecl(hp).flags then writer.AsmWrite(' appending'); WriteLinkageVibilityFlags(taillvmdecl(hp).namesym.bind); writer.AsmWrite(' '); if (ldf_tls in taillvmdecl(hp).flags) then writer.AsmWrite('thread_local '); if ldf_unnamed_addr in taillvmdecl(hp).flags then writer.AsmWrite('unnamed_addr '); if taillvmdecl(hp).sec in [sec_rodata,sec_rodata_norel] then writer.AsmWrite('constant ') else writer.AsmWrite('global '); if not assigned(taillvmdecl(hp).initdata) then begin writer.AsmWrite(llvmencodetypename(taillvmdecl(hp).def)); if not(taillvmdecl(hp).namesym.bind in [AB_EXTERNAL, AB_WEAK_EXTERNAL,AB_EXTERNAL_INDIRECT]) then writer.AsmWrite(' zeroinitializer'); end else begin inc(fdecllevel); { can't have an external symbol with initialisation data } if taillvmdecl(hp).namesym.bind in [AB_EXTERNAL, AB_WEAK_EXTERNAL] then internalerror(2014052905); { bitcast initialisation data to the type of the constant } { write initialisation data } hp2:=tai(taillvmdecl(hp).initdata.first); while assigned(hp2) do begin WriteTai(replaceforbidden,do_line,InlineLevel,asmblock,hp2); hp2:=tai(hp2.next); end; dec(fdecllevel); end; { custom section name? } case taillvmdecl(hp).sec of sec_user: begin writer.AsmWrite(', section "'); writer.AsmWrite(taillvmdecl(hp).secname); writer.AsmWrite('"'); end; low(TObjCAsmSectionType)..high(TObjCAsmSectionType): begin writer.AsmWrite(', section "'); writer.AsmWrite(objc_section_name(taillvmdecl(hp).sec)); writer.AsmWrite('"'); end; else ; end; { sections whose name starts with 'llvm.' are for LLVM internal use and don't have an alignment } if pos('llvm.',taillvmdecl(hp).secname)<>1 then begin { alignment } writer.AsmWrite(', align '); writer.AsmWriteln(tostr(taillvmdecl(hp).alignment)); end else writer.AsmLn; end; end; ait_llvmalias: begin writer.AsmWrite(LlvmAsmSymName(taillvmalias(hp).newsym)); writer.AsmWrite(' = alias '); WriteLinkageVibilityFlags(taillvmalias(hp).bind); if taillvmalias(hp).def.typ=procdef then sstr:=llvmencodeproctype(tabstractprocdef(taillvmalias(hp).def), '', lpd_alias) else sstr:=llvmencodetypename(taillvmalias(hp).def); writer.AsmWrite(sstr); if llvmflag_alias_double_type in llvmversion_properties[current_settings.llvmversion] then begin writer.AsmWrite(', '); writer.AsmWrite(sstr); end; writer.AsmWrite('* '); writer.AsmWriteln(LlvmAsmSymName(taillvmalias(hp).oldsym)); end; ait_symbolpair: begin { should be emitted as part of the symbol def } internalerror(2013010708); end; ait_symbol_end : begin if tai_symbol_end(hp).sym.typ=AT_FUNCTION then writer.AsmWriteln('}') else writer.AsmWriteln('; ait_symbol_end error, should not be generated'); // internalerror(2013010711); end; ait_instruction : begin WriteInstruction(hp); end; ait_llvmins: begin WriteLlvmInstruction(hp); end; ait_stab : begin internalerror(2013010712); end; ait_force_line, ait_function_name : ; ait_cutobject : begin end; ait_marker : case tai_marker(hp).kind of mark_NoLineInfoStart: inc(InlineLevel); mark_NoLineInfoEnd: dec(InlineLevel); { these cannot be nested } mark_AsmBlockStart: asmblock:=true; mark_AsmBlockEnd: asmblock:=false; else ; end; ait_directive : begin { CPU directive is commented out for the LLVM } if tai_directive(hp).directive=asd_cpu then writer.AsmWrite(asminfo^.comment); WriteDirectiveName(tai_directive(hp).directive); if tai_directive(hp).name <>'' then writer.AsmWrite(tai_directive(hp).name); if fdecllevel<>0 then internalerror(2015090602); writer.AsmLn; end; ait_seh_directive : begin internalerror(2013010713); end; ait_varloc: begin if tai_varloc(hp).newlocationhi<>NR_NO then writer.AsmWrite(strpnew('Var '+tai_varloc(hp).varsym.realname+' located in register '+ std_regname(tai_varloc(hp).newlocationhi)+':'+std_regname(tai_varloc(hp).newlocation))) else writer.AsmWrite(strpnew('Var '+tai_varloc(hp).varsym.realname+' located in register '+ std_regname(tai_varloc(hp).newlocation))); if fdecllevel<>0 then internalerror(2015090603); writer.AsmLn; end; ait_typedconst: begin WriteTypedConstData(tai_abstracttypedconst(hp)); end else internalerror(2006012201); end; end; constructor TLLVMAssember.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); begin inherited; InstrWriter:=TLLVMInstrWriter.create(self); end; procedure TLLVMAssember.WriteDirectiveName(dir: TAsmDirective); begin writer.AsmWrite('.'+directivestr[dir]+' '); end; procedure TLLVMAssember.WriteAsmList; var hal : tasmlisttype; i: longint; a: TExternalAssembler; decorator: TLLVMModuleInlineAssemblyDecorator; begin WriteExtraHeader; for hal:=low(TasmlistType) to high(TasmlistType) do begin if not assigned(current_asmdata.asmlists[hal]) or current_asmdata.asmlists[hal].Empty then continue; writer.AsmWriteLn(asminfo^.comment+'Begin asmlist '+AsmlistTypeStr[hal]); if hal<>al_pure_assembler then writetree(current_asmdata.asmlists[hal]) else begin { write routines using the target-specific external assembler writer, filtered using the LLVM module-level assembly decorator } decorator:=TLLVMModuleInlineAssemblyDecorator.Create; writer.decorator:=decorator; a:=GetExternalGnuAssemblerWithAsmInfoWriter(asminfo,writer); a.WriteTree(current_asmdata.asmlists[hal]); writer.decorator:=nil; decorator.free; a.free; end; writer.AsmWriteLn(asminfo^.comment+'End asmlist '+AsmlistTypeStr[hal]); end; writer.AsmLn; end; procedure TLLVMAssember.WriteFunctionInlineAsmList(list: tasmlist); var a: TExternalAssembler; begin if not assigned(ffuncinlasmdecorator) then ffuncinlasmdecorator:=TLLVMFunctionInlineAssemblyDecorator.create; if assigned(writer.decorator) then internalerror(2016110201); writer.decorator:=ffuncinlasmdecorator; a:=GetExternalGnuAssemblerWithAsmInfoWriter(asminfo,writer); a.WriteTree(list); a.free; writer.decorator:=nil; end; {****************************************************************************} { LLVM Instruction Writer } {****************************************************************************} constructor TLLVMInstrWriter.create(_owner: TLLVMAssember); begin inherited create; owner := _owner; end; const as_llvm_info : tasminfo = ( id : as_llvm; idtxt : 'LLVM-AS'; asmbin : 'llc'; asmcmd: '$OPT -o $OBJ $ASM'; supported_targets : [system_x86_64_linux,system_x86_64_darwin,system_powerpc64_darwin]; flags : [af_smartlink_sections]; labelprefix : 'L'; comment : '; '; dollarsign: '$'; ); begin RegisterAssembler(as_llvm_info,TLLVMAssember); end.