From 0a915e883ef0479aaca0db5ae9b68c2f17ecff34 Mon Sep 17 00:00:00 2001
From: svenbarth <pascaldragon@googlemail.com>
Date: Thu, 21 Nov 2019 21:44:53 +0000
Subject: [PATCH] * keep track of static symbols that a global function
 references, as those must now be exported from a dynamic package as well if
 the function can potentially be inlined

git-svn-id: trunk@43544 -
---
 compiler/htypechk.pas              |  4 +-
 compiler/ncal.pas                  |  2 +-
 compiler/ncnv.pas                  |  2 +-
 compiler/nflw.pas                  |  2 +-
 compiler/pkgutil.pas               |  7 +++-
 compiler/procinfo.pas              | 62 +++++++++++++++++++++++++++++-
 compiler/psub.pas                  |  2 +
 compiler/symconst.pas              |  8 +++-
 compiler/symtable.pas              | 48 +++++++++++++++++++++--
 compiler/utils/ppuutils/ppudump.pp |  6 ++-
 10 files changed, 127 insertions(+), 16 deletions(-)

diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
index 2a9f378f75..5bb207cc89 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -857,7 +857,7 @@ implementation
             exit;
           end;
 
-        addsymref(operpd.procsym);
+        addsymref(operpd.procsym,operpd);
 
         { the nil as symtable signs firstcalln that this is
           an overloaded operator }
@@ -1052,7 +1052,7 @@ implementation
             exit;
           end;
 
-        addsymref(operpd.procsym);
+        addsymref(operpd.procsym,operpd);
 
         { the nil as symtable signs firstcalln that this is
           an overloaded operator }
diff --git a/compiler/ncal.pas b/compiler/ncal.pas
index a85e12c3cf..27d863c8a2 100644
--- a/compiler/ncal.pas
+++ b/compiler/ncal.pas
@@ -3787,7 +3787,7 @@ implementation
             { add reference to corresponding procsym; may not be the one
               originally found/passed to the constructor because of overloads }
             if procdefinition.typ = procdef then
-              addsymref(tprocdef(procdefinition).procsym);
+              addsymref(tprocdef(procdefinition).procsym,procdefinition);
 
             { add needed default parameters }
             if (paralength<procdefinition.maxparacount) then
diff --git a/compiler/ncnv.pas b/compiler/ncnv.pas
index 9a7e658a50..4e32164e82 100644
--- a/compiler/ncnv.pas
+++ b/compiler/ncnv.pas
@@ -2513,7 +2513,7 @@ implementation
               te_convert_operator :
                 begin
                   include(current_procinfo.flags,pi_do_call);
-                  addsymref(aprocdef.procsym);
+                  addsymref(aprocdef.procsym,aprocdef);
                   hp:=ccallnode.create(ccallparanode.create(left,nil),Tprocsym(aprocdef.procsym),nil,nil,[],nil);
                   { tell explicitly which def we must use !! (PM) }
                   tcallnode(hp).procdefinition:=aprocdef;
diff --git a/compiler/nflw.pas b/compiler/nflw.pas
index 9a00608288..8a5a3c0081 100644
--- a/compiler/nflw.pas
+++ b/compiler/nflw.pas
@@ -793,7 +793,7 @@ implementation
             enum_get_params:=ccallparanode.create(expr.getcopy,nil);
             enum_get:=ccallnode.create(enum_get_params, tprocsym(enumerator_get.procsym), nil, nil, [],nil);
             tcallnode(enum_get).procdefinition:=enumerator_get;
-            addsymref(enumerator_get.procsym);
+            addsymref(enumerator_get.procsym,enumerator_get);
           end
         else
           enum_get:=ccallnode.create(nil, tprocsym(enumerator_get.procsym), enumerator_get.owner, expr.getcopy, [],nil);
diff --git a/compiler/pkgutil.pas b/compiler/pkgutil.pas
index b61ddd55e0..7f17e51c16 100644
--- a/compiler/pkgutil.pas
+++ b/compiler/pkgutil.pas
@@ -88,7 +88,10 @@ implementation
                 (symtable.symtabletype in [globalsymtable,recordsymtable,objectsymtable]) or
                 (
                   (symtable.symtabletype=staticsymtable) and
-                  ([po_public,po_has_public_name]*pd.procoptions<>[])
+                  (
+                    ([po_public,po_has_public_name]*pd.procoptions<>[]) or
+                    (df_has_global_ref in pd.defoptions)
+                  )
                 )
               ) then
             begin
@@ -217,7 +220,7 @@ implementation
           end;
         staticvarsym:
           begin
-            if publiconly and not (vo_is_public in tstaticvarsym(sym).varoptions) then
+            if publiconly and ([vo_is_public,vo_has_global_ref]*tstaticvarsym(sym).varoptions=[]) then
               exit;
             varexport(tsym(sym).mangledname);
           end;
diff --git a/compiler/procinfo.pas b/compiler/procinfo.pas
index 02e073a43f..7279dd05c7 100644
--- a/compiler/procinfo.pas
+++ b/compiler/procinfo.pas
@@ -140,6 +140,11 @@ unit procinfo;
             need to be checked explicitly like on RISC-V or certain ARM architectures }
           FPUExceptionCheckNeeded : Boolean;
 
+          { local symbols and defs referenced by global functions; these need
+            to be exported in case the function gets inlined }
+          localrefsyms : tfpobjectlist;
+          localrefdefs : tfpobjectlist;
+
           constructor create(aparent:tprocinfo);virtual;
           destructor destroy;override;
 
@@ -170,6 +175,11 @@ unit procinfo;
           function has_nestedprocs: boolean;
           function get_normal_proc: tprocinfo;
 
+          procedure add_local_ref_sym(sym:tsym);
+          procedure export_local_ref_syms;
+          procedure add_local_ref_def(def:tdef);
+          procedure export_local_ref_defs;
+
           function create_for_outlining(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef; entrynodeinfo: tnode): tprocinfo;
 
           { Add to parent's list of nested procedures even if parent is a 'main' procedure }
@@ -203,7 +213,7 @@ unit procinfo;
 implementation
 
     uses
-      globals,cutils,systems,
+      globals,cutils,systems,verbose,
       procdefutil;
 
 {****************************************************************************
@@ -244,6 +254,8 @@ implementation
          nestedprocs.free;
          aktproccode.free;
          aktlocaldata.free;
+         localrefsyms.free;
+         localrefdefs.free;
       end;
 
     procedure tprocinfo.destroy_tree;
@@ -288,6 +300,54 @@ implementation
           result:=result.parent;
       end;
 
+    procedure tprocinfo.add_local_ref_sym(sym:tsym);
+      begin
+        if not assigned(localrefsyms) then
+          localrefsyms:=tfpobjectlist.create(false);
+        if localrefsyms.indexof(sym)<0 then
+          localrefsyms.add(sym);
+      end;
+
+    procedure tprocinfo.export_local_ref_syms;
+      var
+        i : longint;
+        sym : tsym;
+      begin
+        if not assigned(localrefsyms) then
+          exit;
+        for i:=0 to localrefsyms.count-1 do
+          begin
+            sym:=tsym(localrefsyms[i]);
+            if sym.typ<>staticvarsym then
+              internalerror(2019110901);
+            include(tstaticvarsym(sym).varoptions,vo_has_global_ref);
+          end;
+      end;
+
+    procedure tprocinfo.add_local_ref_def(def:tdef);
+      begin
+        if not assigned(localrefdefs) then
+          localrefdefs:=tfpobjectlist.create(false);
+        if localrefdefs.indexof(def)<0 then
+          localrefdefs.add(def);
+      end;
+
+    procedure tprocinfo.export_local_ref_defs;
+      var
+        i : longint;
+        def : tdef;
+      begin
+        if not assigned(localrefdefs) then
+          exit;
+        for i:=0 to localrefdefs.count-1 do
+          begin
+            def:=tdef(localrefdefs[i]);
+            if def.typ<>symconst.procdef then
+              internalerror(2019111801);
+            include(tprocdef(def).defoptions,df_has_global_ref);
+          end;
+      end;
+
     function tprocinfo.create_for_outlining(const basesymname: string; astruct: tabstractrecorddef; potype: tproctypeoption; resultdef: tdef; entrynodeinfo: tnode): tprocinfo;
       begin
         result:=cprocinfo.create(self);
diff --git a/compiler/psub.pas b/compiler/psub.pas
index 4cebaff3ac..641e35df98 100644
--- a/compiler/psub.pas
+++ b/compiler/psub.pas
@@ -1384,6 +1384,8 @@ implementation
         if procdef.inlininginfo^.code.nodetype=blockn then
           include(procdef.inlininginfo^.code.flags,nf_block_with_exit);
         procdef.has_inlininginfo:=true;
+        export_local_ref_syms;
+        export_local_ref_defs;
        end;
 
     procedure searchthreadvar(p: TObject; arg: pointer);
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index 07e1659a07..e56ba0e3c9 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -232,7 +232,9 @@ type
       because we have to access this information in the symtable unit }
     df_llvm_no_struct_packing,
     { internal def that's not for any export }
-    df_internal
+    df_internal,
+    { the local def is referenced from a public function }
+    df_has_global_ref
   );
   tdefoptions=set of tdefoption;
 
@@ -607,7 +609,9 @@ type
       sections }
     vo_is_default_var,
     { i8086 'external far' (can only be used in combination with vo_is_external) }
-    vo_is_far
+    vo_is_far,
+    { a static symbol that is referenced from a global function }
+    vo_has_global_ref
   );
   tvaroptions=set of tvaroption;
 
diff --git a/compiler/symtable.pas b/compiler/symtable.pas
index 9c58e36a40..1ce4a69890 100644
--- a/compiler/symtable.pas
+++ b/compiler/symtable.pas
@@ -334,7 +334,8 @@ interface
     procedure write_system_parameter_lists(const name:string);
 
 {*** Search ***}
-    procedure addsymref(sym:tsym);
+    procedure addsymref(sym:tsym);inline;
+    procedure addsymref(sym:tsym;def:tdef);
     function  is_owned_by(nesteddef,ownerdef:tdef):boolean;
     function  sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean;
     function  defs_belong_to_same_generic(def1,def2:tdef):boolean;
@@ -474,7 +475,7 @@ implementation
 
     uses
       { global }
-      verbose,globals,
+      verbose,globals,systems,
       { symtable }
       symutil,defutil,defcmp,objcdef,
       { module }
@@ -3029,9 +3030,9 @@ implementation
                                   Search
 *****************************************************************************}
 
-     procedure addsymref(sym:tsym);
+     procedure addsymref(sym:tsym;def:tdef);
        var
-         owner: tsymtable;
+         owner,procowner : tsymtable;
        begin
          { for symbols used in preprocessor expressions, we don't want to
            increase references count (for smaller final binaries) }
@@ -3063,8 +3064,47 @@ implementation
                  { symbol is imported from another unit }
                  current_module.addimportedsym(sym);
              end;
+         { static symbols that are used in public functions must be exported
+           for packages as well }
+         if ([tf_supports_packages,tf_supports_hidden_symbols]<=target_info.flags) and
+             (owner.symtabletype=staticsymtable) and
+             assigned(current_procinfo) and
+             (
+               (
+                 (sym.typ=staticvarsym) and
+                 ([vo_is_public,vo_has_global_ref]*tstaticvarsym(sym).varoptions=[])
+               ) or (
+                 (sym.typ=localvarsym) and
+                 assigned(tlocalvarsym(sym).defaultconstsym) and
+                 ([vo_is_public,vo_has_global_ref]*tstaticvarsym(tlocalvarsym(sym).defaultconstsym).varoptions=[])
+               ) or (
+                 (sym.typ=procsym) and
+                 assigned(def) and
+                 (def.typ=procdef) and
+                 not (df_has_global_ref in def.defoptions) and
+                 not (po_public in tprocdef(def).procoptions)
+               )
+             ) then
+           begin
+             procowner:=current_procinfo.procdef.owner;
+             while procowner.symtabletype in [objectsymtable,recordsymtable] do
+               procowner:=tdef(procowner.defowner).owner;
+             if procowner.symtabletype=globalsymtable then
+               begin
+                 if sym.typ=procsym then
+                   current_procinfo.add_local_ref_def(def)
+                 else if sym.typ=staticvarsym then
+                   current_procinfo.add_local_ref_sym(sym)
+                 else
+                   current_procinfo.add_local_ref_sym(tlocalvarsym(sym).defaultconstsym);
+               end;
+           end;
        end;
 
+     procedure addsymref(sym:tsym);
+       begin
+         addsymref(sym,nil);
+       end;
 
     function is_owned_by(nesteddef,ownerdef:tdef):boolean;
       begin
diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
index 6940791192..50005c68a9 100644
--- a/compiler/utils/ppuutils/ppudump.pp
+++ b/compiler/utils/ppuutils/ppudump.pp
@@ -2729,7 +2729,8 @@ const
      { this should never happen for defs stored to a ppu file }
      (mask:df_not_registered_no_free;  str:'Unregistered/No free (invalid)'),
      (mask:df_llvm_no_struct_packing;  str:'LLVM unpacked struct'),
-     (mask:df_internal;       str:'Internal')
+     (mask:df_internal;       str:'Internal'),
+     (mask:df_has_global_ref; str:'Has Global Ref')
   );
   defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
      (mask:ds_vmt_written;           str:'VMT Written'),
@@ -3068,7 +3069,8 @@ const
      (mask:vo_has_section;     str:'HasSection'),
      (mask:vo_force_finalize;  str:'ForceFinalize'),
      (mask:vo_is_default_var;  str:'DefaultIntrinsicVar'),
-     (mask:vo_is_far;          str:'IsFar')
+     (mask:vo_is_far;          str:'IsFar'),
+     (mask:vo_has_global_ref;  str:'HasGlobalRef')
   );
 type
   tvaraccessdesc=record