From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1873) id D0F2C385736C; Tue, 9 Aug 2022 12:41:45 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D0F2C385736C MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Iain Buclaw To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-2002] d: Fix undefined reference to pragma(inline) symbol (PR106563) X-Act-Checkin: gcc X-Git-Author: Iain Buclaw X-Git-Refname: refs/heads/master X-Git-Oldrev: 4e1914625dec4aa09a5671c6294e877dbf4518f5 X-Git-Newrev: 04284176d549ff2565406406a6d53ab4ba8e507d Message-Id: <20220809124145.D0F2C385736C@sourceware.org> Date: Tue, 9 Aug 2022 12:41:45 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 09 Aug 2022 12:41:45 -0000 https://gcc.gnu.org/g:04284176d549ff2565406406a6d53ab4ba8e507d commit r13-2002-g04284176d549ff2565406406a6d53ab4ba8e507d Author: Iain Buclaw Date: Tue Aug 9 12:48:14 2022 +0200 d: Fix undefined reference to pragma(inline) symbol (PR106563) Functions that are declared `pragma(inline)' should be treated as if they are defined in every translation unit they are referenced from, regardless of visibility protection. Ensure they always get DECL_ONE_ONLY linkage, and start emitting them into other modules that import them. PR d/106563 gcc/d/ChangeLog: * decl.cc (DeclVisitor::visit (FuncDeclaration *)): Set semanticRun before generating its symbol. (function_defined_in_root_p): New function. (function_needs_inline_definition_p): New function. (maybe_build_decl_tree): New function. (get_symbol_decl): Call maybe_build_decl_tree before returning symbol. (start_function): Use function_defined_in_root_p instead of inline test for locally defined symbols. (set_linkage_for_decl): Check for inline functions before private or protected symbols. gcc/testsuite/ChangeLog: * gdc.dg/torture/torture.exp (srcdir): New proc. * gdc.dg/torture/imports/pr106563math.d: New test. * gdc.dg/torture/imports/pr106563regex.d: New test. * gdc.dg/torture/imports/pr106563uni.d: New test. * gdc.dg/torture/pr106563.d: New test. Diff: --- gcc/d/decl.cc | 121 +++++++++++++++++---- .../gdc.dg/torture/imports/pr106563math.d | 12 ++ .../gdc.dg/torture/imports/pr106563regex.d | 7 ++ gcc/testsuite/gdc.dg/torture/imports/pr106563uni.d | 15 +++ gcc/testsuite/gdc.dg/torture/pr106563.d | 16 +++ gcc/testsuite/gdc.dg/torture/torture.exp | 9 ++ 6 files changed, 161 insertions(+), 19 deletions(-) diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 58cea4dec3f..0131b01dcc9 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -828,6 +828,10 @@ public: if (global.errors) return; + /* Start generating code for this function. */ + gcc_assert (d->semanticRun == PASS::semantic3done); + d->semanticRun = PASS::obj; + /* Duplicated FuncDeclarations map to the same symbol. Check if this is the one declaration which will be emitted. */ tree fndecl = get_symbol_decl (d); @@ -844,10 +848,6 @@ public: if (global.params.verbose) message ("function %s", d->toPrettyChars ()); - /* Start generating code for this function. */ - gcc_assert (d->semanticRun == PASS::semantic3done); - d->semanticRun = PASS::obj; - tree old_context = start_function (d); tree parm_decl = NULL_TREE; @@ -1020,13 +1020,103 @@ build_decl_tree (Dsymbol *d) input_location = saved_location; } +/* Returns true if function FD is defined or instantiated in a root module. */ + +static bool +function_defined_in_root_p (FuncDeclaration *fd) +{ + Module *md = fd->getModule (); + if (md && md->isRoot ()) + return true; + + TemplateInstance *ti = fd->isInstantiated (); + if (ti && ti->minst && ti->minst->isRoot ()) + return true; + + return false; +} + +/* Returns true if function FD always needs to be implicitly defined, such as + it was declared `pragma(inline)'. */ + +static bool +function_needs_inline_definition_p (FuncDeclaration *fd) +{ + /* Function has already been defined. */ + if (!DECL_EXTERNAL (fd->csym)) + return false; + + /* Non-inlineable functions are always external. */ + if (DECL_UNINLINABLE (fd->csym)) + return false; + + /* No function body available for inlining. */ + if (!fd->fbody) + return false; + + /* Ignore functions that aren't decorated with `pragma(inline)'. */ + if (fd->inlining != PINLINE::always) + return false; + + /* These functions are tied to the module they are defined in. */ + if (fd->isFuncLiteralDeclaration () + || fd->isUnitTestDeclaration () + || fd->isFuncAliasDeclaration () + || fd->isInvariantDeclaration ()) + return false; + + /* Check whether function will be regularly defined later in the current + translation unit. */ + if (function_defined_in_root_p (fd)) + return false; + + /* Weak functions cannot be inlined. */ + if (lookup_attribute ("weak", DECL_ATTRIBUTES (fd->csym))) + return false; + + /* Naked functions cannot be inlined. */ + if (lookup_attribute ("naked", DECL_ATTRIBUTES (fd->csym))) + return false; + + return true; +} + +/* If the variable or function declaration in DECL needs to be defined, call + build_decl_tree on it now before returning its back-end symbol. */ + +static tree +maybe_build_decl_tree (Declaration *decl) +{ + gcc_assert (decl->csym != NULL_TREE); + + /* Still running semantic analysis on declaration, or it has already had its + code generated. */ + if (doing_semantic_analysis_p || decl->semanticRun >= PASS::obj) + return decl->csym; + + if (error_operand_p (decl->csym)) + return decl->csym; + + if (FuncDeclaration *fd = decl->isFuncDeclaration ()) + { + /* Externally defined inline functions need to be emitted. */ + if (function_needs_inline_definition_p (fd)) + { + DECL_EXTERNAL (fd->csym) = 0; + build_decl_tree (fd); + } + } + + return decl->csym; +} + /* Return the decl for the symbol, create it if it doesn't already exist. */ tree get_symbol_decl (Declaration *decl) { if (decl->csym) - return decl->csym; + return maybe_build_decl_tree (decl); /* Deal with placeholder symbols immediately: SymbolDeclaration is used as a shell around an initializer symbol. */ @@ -1404,7 +1494,7 @@ get_symbol_decl (Declaration *decl) TREE_USED (decl->csym) = 1; d_keep (decl->csym); - return decl->csym; + return maybe_build_decl_tree (decl); } /* Returns a declaration for a VAR_DECL. Used to create compiler-generated @@ -1895,15 +1985,8 @@ start_function (FuncDeclaration *fd) /* Function has been defined, check now whether we intend to send it to object file, or it really is extern. Such as inlinable functions from modules not in this compilation, or thunk aliases. */ - TemplateInstance *ti = fd->isInstantiated (); - if (ti && ti->needsCodegen ()) + if (function_defined_in_root_p (fd)) DECL_EXTERNAL (fndecl) = 0; - else - { - Module *md = fd->getModule (); - if (md && md->isRoot ()) - DECL_EXTERNAL (fndecl) = 0; - } DECL_INITIAL (fndecl) = error_mark_node; @@ -2422,16 +2505,16 @@ set_linkage_for_decl (tree decl) if (!TREE_PUBLIC (decl)) return; - /* Don't need to give private or protected symbols a special linkage. */ - if ((TREE_PRIVATE (decl) || TREE_PROTECTED (decl)) - && !DECL_INSTANTIATED (decl)) - return; - /* Functions declared as `pragma(inline, true)' can appear in multiple translation units. */ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl)) return d_comdat_linkage (decl); + /* Don't need to give private or protected symbols a special linkage. */ + if ((TREE_PRIVATE (decl) || TREE_PROTECTED (decl)) + && !DECL_INSTANTIATED (decl)) + return; + /* If all instantiations must go in COMDAT, give them that linkage. This also applies to other extern declarations, so that it is possible for them to override template declarations. */ diff --git a/gcc/testsuite/gdc.dg/torture/imports/pr106563math.d b/gcc/testsuite/gdc.dg/torture/imports/pr106563math.d new file mode 100644 index 00000000000..b9351ea74e2 --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/imports/pr106563math.d @@ -0,0 +1,12 @@ +module imports.pr106563math; + +T nextPow2(T)(const T val) +{ + return powIntegralImpl(val); +} + +pragma(inline, true) +T powIntegralImpl(T)(T) +{ + return 1; +} diff --git a/gcc/testsuite/gdc.dg/torture/imports/pr106563regex.d b/gcc/testsuite/gdc.dg/torture/imports/pr106563regex.d new file mode 100644 index 00000000000..a2cd90c3d8b --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/imports/pr106563regex.d @@ -0,0 +1,7 @@ +module imports.pr106563regex; +import imports.pr106563uni; + +struct CharMatcher +{ + typeof(MultiArray!().length) trie; +} diff --git a/gcc/testsuite/gdc.dg/torture/imports/pr106563uni.d b/gcc/testsuite/gdc.dg/torture/imports/pr106563uni.d new file mode 100644 index 00000000000..16e3bc85cc0 --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/imports/pr106563uni.d @@ -0,0 +1,15 @@ +module imports.pr106563uni; + +struct MultiArray() +{ + @property length() + { + return spaceFor!0(); + } +} + +size_t spaceFor(size_t bits)() +{ + import imports.pr106563math; + return nextPow2(bits); +} diff --git a/gcc/testsuite/gdc.dg/torture/pr106563.d b/gcc/testsuite/gdc.dg/torture/pr106563.d new file mode 100644 index 00000000000..7e15442c0bb --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/pr106563.d @@ -0,0 +1,16 @@ +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106563 +// { dg-do link } +// { dg-additional-files "imports/pr106563math.d imports/pr106563regex.d imports/pr106563uni.d" } +// { dg-additional-options "-I[srcdir] -fno-druntime" } +import imports.pr106563math; +import imports.pr106563regex; + +auto requireSize()(size_t size) +{ + return nextPow2(size); +} + +extern(C) int main() +{ + return cast(int)requireSize(0); +} diff --git a/gcc/testsuite/gdc.dg/torture/torture.exp b/gcc/testsuite/gdc.dg/torture/torture.exp index f7d00b1b052..d9c6a79cfe2 100644 --- a/gcc/testsuite/gdc.dg/torture/torture.exp +++ b/gcc/testsuite/gdc.dg/torture/torture.exp @@ -19,6 +19,15 @@ # Load support procs. load_lib gdc-dg.exp +# Helper function allows adding tests that use imports/*, but don't compile +# the sources in with dg-additional-sources. +global testdir +set testdir $srcdir/$subdir +proc srcdir {} { + global testdir + return $testdir +} + # The default option list can be overridden by # TORTURE_OPTIONS="{ { list1 } ... { listN } }"