Răsfoiți Sursa

* generate LLVM function-level inline assembly blocks, replacing
references to local variables and parameters with references to
assembler block constraints/parameters

git-svn-id: trunk@34892 -

Jonas Maebe 8 ani în urmă
părinte
comite
c3aa9e2890
1 a modificat fișierele cu 181 adăugiri și 4 ștergeri
  1. 181 4
      compiler/llvm/nllvmbas.pas

+ 181 - 4
compiler/llvm/nllvmbas.pas

@@ -26,9 +26,28 @@ unit nllvmbas;
 interface
 
     uses
-       nbas,ncgbas;
+       globtype,cclasses,
+       aasmbase,aasmtai,aasmdata,
+       nbas,ncgbas,
+       symsym;
 
     type
+       tllvmasmnode = class(tcgasmnode)
+        protected
+         { map a tasmsymbol to an index in fsymboldata }
+         fsymbollookup: THashSet;
+         { the LLVM symbolic inline assembly operands to which the ones in the
+           source code are mapped }
+         fsymboldata: tfplist;
+         function getllvmasmopindexforsym(sym: tabstractnormalvarsym): longint;
+         function getllvmasmparasym(sym: tabstractnormalvarsym): tasmsymbol;
+         procedure ResolveRef(const filepos: tfileposinfo; var op: toper); override;
+        public
+         constructor create(p : TAsmList); override;
+         destructor destroy; override;
+         procedure pass_generate_code; override;
+       end;
+
       tllvmtempinfoaccessor = class(ttempinfoaccessor)
        protected
         class procedure settempinfoflags(tempinfo: ptempinfo; const flags: ttempinfoflags); override;
@@ -41,11 +60,168 @@ interface
   implementation
 
     uses
-      aasmdata,
-      cgbase,cgutils,
-      llvmbase,aasmllvm
+      verbose,cutils,
+      cgbase,cgutils,paramgr,
+      symconst,symdef,procinfo,
+      node,
+      cpubase,llvmbase,aasmllvm
       ;
 
+{*****************************************************************************
+                             TLLVMASMNODE
+*****************************************************************************}
+
+    function tllvmasmnode.getllvmasmopindexforsym(sym: tabstractnormalvarsym): longint;
+      var
+        key: record
+          sym: pointer;
+        end;
+        res: PHashSetItem;
+        callpara: pllvmcallpara;
+      begin
+        key.sym:=sym;
+        res:=fsymbollookup.FindOrAdd(@key,sizeof(key));
+        if not assigned(res^.Data) then
+          begin
+            new(callpara);
+            callpara^.def:=cpointerdef.getreusable(sym.vardef);
+            if (sym.typ=paravarsym) and
+               paramanager.push_addr_param(sym.varspez,sym.vardef,current_procinfo.procdef.proccalloption) then
+              callpara^.def:=cpointerdef.getreusable(callpara^.def);
+            callpara^.sret:=false;
+            callpara^.byval:=false;
+            callpara^.valueext:=lve_none;
+            callpara^.loc:=LOC_REGISTER;
+            { address must be a temp register }
+            if (sym.localloc.loc<>LOC_REFERENCE) or
+               (sym.localloc.reference.base=NR_NO) or
+               (sym.localloc.reference.index<>NR_NO) or
+               (sym.localloc.reference.offset<>0) or
+               assigned(sym.localloc.reference.symbol) then
+              internalerror(2016111001);
+            callpara^.reg:=sym.localloc.reference.base;
+            fsymboldata.add(callpara);
+            ptruint(res^.Data):=fsymboldata.count-1;
+          end;
+        result:=longint(ptruint(res^.Data));
+      end;
+
+
+    function tllvmasmnode.getllvmasmparasym(sym: tabstractnormalvarsym): tasmsymbol;
+      begin
+        result:=current_asmdata.RefAsmSymbol('$'+tostr(getllvmasmopindexforsym(sym)),AT_DATA,false);
+      end;
+
+
+    procedure tllvmasmnode.ResolveRef(const filepos: tfileposinfo; var op: toper);
+      var
+        sym: tabstractnormalvarsym;
+        ref: treference;
+        sofs: pint;
+        indexreg : tregister;
+        getoffset: boolean;
+{$ifdef x86}
+        scale : byte;
+{$endif x86}
+      begin
+        { pure assembler routines are handled by the regular code generator }
+        if po_assembler in current_procinfo.procdef.procoptions then
+          begin
+            inherited;
+            exit;
+          end;
+        { translate all symbolic references to "parameters" of the llvm
+          assembler statements }
+        case op.typ of
+          top_local:
+            begin
+              sofs:=op.localoper^.localsymofs;
+              indexreg:=op.localoper^.localindexreg;
+{$ifdef x86}
+              scale:=op.localoper^.localscale;
+{$endif x86}
+              getoffset:=op.localoper^.localgetoffset;
+              sym:=tabstractnormalvarsym(op.localoper^.localsym);
+              dispose(op.localoper);
+              case sym.localloc.loc of
+                LOC_REFERENCE:
+                  begin
+                    if getoffset then
+                      begin
+                        { todo: print proper error. You cannot get the offset
+                          of a local variable since it may be in a register
+                          outside the assembler block with llvm }
+                        internalerror(2016102001);
+                      end
+                    else
+                      begin
+                        op.typ:=top_ref;
+                        new(op.ref);
+                        reference_reset_symbol(op.ref^,getllvmasmparasym(sym),sofs,
+                          newalignment(sym.localloc.reference.alignment,sofs));
+                        op.ref^.index:=indexreg;
+{$ifdef x86}
+                        op.ref^.scalefactor:=scale;
+{$endif x86}
+                      end;
+                  end
+                else
+                  { all locals accessed from assembler are forced into memory
+                    by FPC }
+                  internalerror(2016101506);
+              end;
+            end;
+        end;
+      end;
+
+
+    constructor tllvmasmnode.create(p: TAsmList);
+      begin
+        inherited;
+      end;
+
+
+    destructor tllvmasmnode.destroy;
+      begin
+        { normally already freed in pass_generate_code, but in case an error
+          occurred that may not have happened }
+        fsymboldata.free;
+        fsymbollookup.free;
+        inherited;
+      end;
+
+
+    procedure tllvmasmnode.pass_generate_code;
+      var
+        oldasmlist: tasmlist;
+        asmai: tai;
+      begin
+        oldasmlist:=nil;
+        if not(po_assembler in current_procinfo.procdef.procoptions) and
+           not(nf_get_asm_position in flags) then
+          begin
+            { store the assembler code in a separate list, so we can make it
+              the argument of an asmblock instruction }
+            oldasmlist:=current_asmdata.CurrAsmList;
+            current_asmdata.CurrAsmList:=tasmlist.create;
+            { record relation between parameters and replaced local assembler
+              operands }
+            fsymboldata:=tfplist.create;
+            fsymbollookup:=THashSet.Create(8,True,False);
+          end;
+        inherited;
+        if not(po_assembler in current_procinfo.procdef.procoptions) and
+           not(nf_get_asm_position in flags) then
+          begin
+            asmai:=taillvm.asm_paras(current_asmdata.CurrAsmList,fsymboldata);
+            fsymboldata:=nil;
+            fsymbollookup.free;
+            fsymbollookup:=nil;
+            oldasmlist.concat(asmai);
+            current_asmdata.CurrAsmList:=oldasmlist;
+          end;
+      end;
+
 {*****************************************************************************
                           TLLVMTEMPINFOACCESSOR
 *****************************************************************************}
@@ -85,6 +261,7 @@ interface
 
 
 begin
+   casmnode:=tllvmasmnode;
    ctempinfoaccessor:=tllvmtempinfoaccessor;
    ctempcreatenode:=tllvmtempcreatenode;
 end.