Browse Source

+ llvm parameter manager: it reuses the native parameter manager to create
the paraloc information, and then adds llvm-specific information to the
paralocs
o only partially implemented, in particular function result handling is
not yet there

git-svn-id: branches/hlcgllvm@26040 -

Jonas Maebe 11 years ago
parent
commit
8ede313ba1
4 changed files with 264 additions and 1 deletions
  1. 2 0
      .gitattributes
  2. 195 0
      compiler/llvm/llvmpara.pas
  3. 54 0
      compiler/llvm/llvmsym.pas
  4. 13 1
      compiler/parabase.pas

+ 2 - 0
.gitattributes

@@ -319,6 +319,8 @@ compiler/llvm/cgllvm.pas svneol=native#text/plain
 compiler/llvm/itllvm.pas svneol=native#text/plain
 compiler/llvm/itllvm.pas svneol=native#text/plain
 compiler/llvm/llvmbase.pas svneol=native#text/plain
 compiler/llvm/llvmbase.pas svneol=native#text/plain
 compiler/llvm/llvminfo.pas svneol=native#text/plain
 compiler/llvm/llvminfo.pas svneol=native#text/plain
+compiler/llvm/llvmpara.pas svneol=native#text/plain
+compiler/llvm/llvmsym.pas svneol=native#text/plain
 compiler/llvm/tgllvm.pas svneol=native#text/plain
 compiler/llvm/tgllvm.pas svneol=native#text/plain
 compiler/m68k/aasmcpu.pas svneol=native#text/plain
 compiler/m68k/aasmcpu.pas svneol=native#text/plain
 compiler/m68k/ag68kgas.pas svneol=native#text/plain
 compiler/m68k/ag68kgas.pas svneol=native#text/plain

+ 195 - 0
compiler/llvm/llvmpara.pas

@@ -0,0 +1,195 @@
+{
+    Copyright (c) 2013 by Jonas Maebe
+
+    Includes the llvm parameter manager
+
+    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 llvmpara;
+
+{$i fpcdefs.inc}
+
+  interface
+
+    uses
+      globtype,aasmdata,
+      symconst,symtype,symdef,symsym,
+      parabase,
+      cpupara;
+
+    type
+      { LLVM stands for "low level code generator", and regarding parameter
+        handling it is indeed very low level. We are responsible for decomposing
+        aggregate parameters into multiple simple parameters in case they have
+        to be passed in special registers (such as floating point or SSE), and
+        also for indicating whether e.g. 8 bit parameters need to be sign or
+        zero exntended. This corresponds to pretty much what we do when creating
+        parameter locations, so we reuse the original parameter manager and then
+        process its output.
+
+        The future will tell whether we can do this without
+        architecture-specific code, or whether we will have to integrate parts
+        into the various tcpuparamanager classes }
+      tllvmparamanager = class(tcpuparamanager)
+        function param_use_paraloc(const cgpara: tcgpara): boolean; override;
+        procedure createtempparaloc(list: TAsmList; calloption: tproccalloption; parasym: tparavarsym; can_use_final_stack_loc: boolean; var cgpara: TCGPara); override;
+        function create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint; override;
+        function get_funcretloc(p: tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara; override;
+       private
+        procedure set_llvm_paraloc_name(p: tabstractprocdef; hp: tparavarsym; var para: tcgpara);
+        procedure add_llvm_callee_paraloc_names(p: tabstractprocdef);
+      end;
+
+
+  implementation
+
+    uses
+      aasmbase,
+      llvmsym,
+      paramgr,
+      cgbase,hlcgobj;
+
+
+  { tllvmparamanager }
+
+  function tllvmparamanager.param_use_paraloc(const cgpara: tcgpara): boolean;
+    begin
+      { we can use the paraloc on the callee side if the SSA property is
+        guaranteed, i.e., if it is a constant location (and if it's not been
+        split up into multiple locations for ABI reasons). We can't deduce that
+        from the paraloc though, we need the parasym for that. Potential
+        future optimisation, although llvm will probably optimise away the
+        temps we create anyway }
+      result:=false;
+    end;
+
+  procedure tllvmparamanager.createtempparaloc(list: TAsmList; calloption: tproccalloption; parasym: tparavarsym; can_use_final_stack_loc: boolean; var cgpara: TCGPara);
+    begin
+      inherited;
+    end;
+
+  function tllvmparamanager.create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint;
+    begin
+      result:=inherited create_paraloc_info(p, side);
+      { on the calleeside, llvm declares the parameters similar to Pascal or C
+        (a list of parameters and their types), but they correspond more
+        closely to parameter locations than to parameters -> add names to the
+        locations }
+      if side=calleeside then
+        add_llvm_callee_paraloc_names(p)
+    end;
+
+  { hp non-nil: parasym to check
+    hp nil: function result
+  }
+  procedure tllvmparamanager.set_llvm_paraloc_name(p: tabstractprocdef; hp: tparavarsym; var para: tcgpara);
+
+    { the default for llvm is to pass aggregates in integer registers or
+      on the stack (as the ABI prescribes). Records that require special
+      handling, e.g. (partly) passing in fpu registers, have to be handled
+      explicitly. This function returns whether an aggregate is handled
+      specially }
+    function has_non_default_paraloc: boolean;
+      var
+        loc: PCGParaLocation;
+      begin
+        loc:=para.Location;
+        result:=true;
+        while assigned(loc) do
+          begin
+            if not(loc^.loc in [LOC_REGISTER,LOC_REFERENCE]) then
+              exit;
+          end;
+        result:=false;
+      end;
+
+    var
+      paraloc: PCGParaLocation;
+      signextstr: TSymStr;
+      usedef: tdef;
+      paralocnr: longint;
+    begin
+      { byval: a pointer to a type that should actually be passed by
+          value (e.g. a record that should be passed on the stack) }
+       if (assigned(hp) and
+           (hp.vardef.typ in [arraydef,recorddef,objectdef]) and
+           not paramanager.push_addr_param(hp.varspez,hp.vardef,p.proccalloption) and
+           not has_non_default_paraloc) or
+          (not assigned(hp) and
+           ret_in_param(para.def,p)) then
+        begin
+          { the first location is the name of the "byval" parameter }
+          paraloc:=para.location;
+          if assigned(hp) then
+             begin
+              paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol(llvmparaname(hp,0),AB_LOCAL,AT_DATA);
+              paraloc^.llvmvalueloc:=false;
+             end
+          else
+            begin
+              paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol('%f.result',AB_LOCAL,AT_DATA);
+              paraloc^.llvmvalueloc:=true;
+            end;
+          { the other locations, if any, are not represented individually; llvm
+            takes care of them behind the scenes }
+          while assigned(paraloc^.next) do
+            begin
+              paraloc:=paraloc^.next;
+              paraloc^.llvmloc:=nil;
+            end;
+          exit;
+        end;
+      paraloc:=hp.paraloc[calleeside].location;
+      paralocnr:=0;
+      repeat
+        paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol(llvmparaname(hp,paralocnr),AB_LOCAL,AT_DATA);
+        paraloc^.llvmvalueloc:=true;
+        paraloc:=paraloc^.next;
+        inc(paralocnr);
+      until not assigned(paraloc);
+    end;
+
+
+  function tllvmparamanager.get_funcretloc(p: tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
+    begin
+      result:=inherited;
+      { TODO: initialise result.llvmloc }
+    end;
+
+    procedure tllvmparamanager.add_llvm_callee_paraloc_names(p: tabstractprocdef);
+      var
+        paranr: longint;
+        para: tcgpara;
+        hp: tparavarsym;
+        first: boolean;
+      begin
+        first:=true;
+        for paranr:=0 to p.paras.count-1 do
+          begin
+            hp:=tparavarsym(p.paras[paranr]);
+            set_llvm_paraloc_name(p,hp,hp.paraloc[calleeside]);
+          end;
+      end;
+
+begin
+  { replace the native code generator. Maybe this has to be moved to a procedure
+    like the creations of the code generators, but possibly not since we still
+    call the original paramanager }
+  paramanager.free;
+  paramanager:=tllvmparamanager.create;
+end.
+

+ 54 - 0
compiler/llvm/llvmsym.pas

@@ -0,0 +1,54 @@
+{
+    Copyright (c) 2013 by Jonas Maebe
+
+    This unit implements some LLVM symbol helper routines.
+
+    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.
+
+ ****************************************************************************
+}
+
+{$i fpcdefs.inc}
+
+unit llvmsym;
+
+interface
+
+  uses
+    globtype,
+    symbase,symtype,symsym;
+
+  function llvmparaname(sym: tparavarsym; paralocnr: longint): TSymStr;
+
+
+implementation
+
+  uses
+      cutils,
+      symconst;
+
+  function llvmparaname(sym: tparavarsym; paralocnr: longint): TSymStr;
+    begin
+      result:='%p.'+sym.realname;
+      { use the same convention as llvm-gcc and clang: if an aggregate parameter
+        is split into multiple locations, suffix each part with '.coerce#' }
+      if assigned(sym.paraloc[calleeside].location^.next) then
+        result:=result+'.coerce'+tostr(paralocnr);
+    end;
+
+
+
+end.
+

+ 13 - 1
compiler/parabase.pas

@@ -26,7 +26,7 @@ unit parabase;
 
 
     uses
     uses
        cclasses,globtype,
        cclasses,globtype,
-       cpubase,cgbase,cgutils,
+       aasmbase,cpubase,cgbase,cgutils,
        symtype, ppu;
        symtype, ppu;
 
 
     type
     type
@@ -41,6 +41,18 @@ unit parabase;
          Size : TCGSize; { size of this location }
          Size : TCGSize; { size of this location }
          Def  : tdef;
          Def  : tdef;
          Loc  : TCGLoc;
          Loc  : TCGLoc;
+{$ifdef llvm}
+         { The following fields are used to determine the name and handling of
+           the location by the llvm code generator. They exist in parallel with
+           the regular information, because that original information is still
+           required for handling inline assembler routines }
+
+         { true if the llvmloc symbol is the value itself, rather than a
+           pointer to the value (~ named register) }
+         llvmvalueloc: boolean;
+         { nil if none corresponding to this particular paraloc }
+         llvmloc: tasmsymbol;
+{$endif llvm}
          case TCGLoc of
          case TCGLoc of
            LOC_REFERENCE : (reference : TCGParaReference);
            LOC_REFERENCE : (reference : TCGParaReference);
            LOC_FPUREGISTER,
            LOC_FPUREGISTER,