mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 14:31:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1474 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1474 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|     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
 | |
|             owner.writer.AsmWrite('i8* blockaddress(');
 | |
|             owner.writer.AsmWrite(getopstr(taillvm(hp).oper[0]^,false));
 | |
|             { getopstr would add a "label" qualifier, which blockaddress does
 | |
|               not want }
 | |
|             owner.writer.AsmWrite(',%');
 | |
|             with taillvm(hp).oper[1]^ 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;
 | |
|       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');
 | |
|           { 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;
 | |
|                   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;
 | |
|               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.
 | 
