diff mbox series

[committed] d: Match function declarations of gcc built-ins from any module.

Message ID 20220613144010.1822566-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Match function declarations of gcc built-ins from any module. | expand

Commit Message

Iain Buclaw June 13, 2022, 2:40 p.m. UTC
This patch changes the `Compiler::onParseModule' hook in the D front-end
to scan for declarations of recognised gcc built-ins from any module.
Previously, only the `core.stdc' package was scanned.

In addition to matching of the symbol, any user-applied `@attributes' or
`pragma(mangle)' name will be applied to the built-in decl as well.
Because there would now be no control over where built-in declarations
are coming from, the warning option `-Wbuiltin-declaration-mismatch' has
been implemented in the D front-end too.

Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and
committed to mainline.

Regards,
Iain.

---
gcc/d/ChangeLog:

	* d-builtins.cc: Include builtins.h.
	(gcc_builtins_libfuncs): Remove.
	(strip_type_modifiers): New function.
	(matches_builtin_type): New function.
	(covariant_with_builtin_type_p): New function.
	(maybe_set_builtin_1): Set front-end built-in if identifier matches
	gcc built-in name.  Apply user-specified attributes and assembler name
	overrides to the built-in.  Warn about built-in declaration mismatches.
	(d_builtin_function): Set IDENTIFIER_DECL_TREE of built-in functions.
	* d-compiler.cc (Compiler::onParseModule): Scan all modules for any
	identifiers that match built-in function names.
	* lang.opt (Wbuiltin-declaration-mismatch): New option.

gcc/testsuite/ChangeLog:

	* gdc.dg/Wbuiltin_declaration_mismatch.d: New test.
	* gdc.dg/builtins.d: New test.
---
 gcc/d/d-builtins.cc                           | 136 ++++++++++++++++--
 gcc/d/d-compiler.cc                           |  40 +++---
 gcc/d/lang.opt                                |   4 +
 .../gdc.dg/Wbuiltin_declaration_mismatch.d    |  37 +++++
 gcc/testsuite/gdc.dg/builtins.d               |  17 +++
 5 files changed, 203 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d
 create mode 100644 gcc/testsuite/gdc.dg/builtins.d
diff mbox series

Patch

diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc
index cd9748c1de1..c2ef0c836e1 100644
--- a/gcc/d/d-builtins.cc
+++ b/gcc/d/d-builtins.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "common/common-target.h"
 #include "stringpool.h"
 #include "stor-layout.h"
+#include "builtins.h"
 
 #include "d-tree.h"
 #include "d-frontend.h"
@@ -44,7 +45,6 @@  along with GCC; see the file COPYING3.  If not see
 
 
 static GTY(()) vec <tree, va_gc> *gcc_builtins_functions = NULL;
-static GTY(()) vec <tree, va_gc> *gcc_builtins_libfuncs = NULL;
 static GTY(()) vec <tree, va_gc> *gcc_builtins_types = NULL;
 
 /* Record built-in types and their associated decls for re-use when
@@ -672,6 +672,87 @@  d_build_builtins_module (Module *m)
   m->members->push (LinkDeclaration::create (Loc (), LINK::c, members));
 }
 
+/* Remove all type modifiers from TYPE, returning the naked type.  */
+
+static Type *
+strip_type_modifiers (Type *type)
+{
+  if (type->ty == TY::Tpointer)
+    {
+      Type *tnext = strip_type_modifiers (type->nextOf ());
+      return tnext->pointerTo ();
+    }
+
+  return type->castMod (0);
+}
+
+/* Returns true if types T1 and T2 representing return types or types of
+   function arguments are close enough to be considered interchangeable.  */
+
+static bool
+matches_builtin_type (Type *t1, Type *t2)
+{
+  Type *tb1 = strip_type_modifiers (t1);
+  Type *tb2 = strip_type_modifiers (t2);
+
+  if (same_type_p (t1, t2))
+    return true;
+
+  if (((tb1->isTypePointer () && tb2->isTypePointer ())
+       || (tb1->isTypeVector () && tb2->isTypeVector ()))
+      && tb1->implicitConvTo (tb2) != MATCH::nomatch)
+    return true;
+
+  if (tb1->isintegral () == tb2->isintegral ()
+      && tb1->size () == tb2->size ())
+    return true;
+
+  return false;
+}
+
+/* Check whether the declared function type T1 is covariant with the built-in
+   function type T2.  Returns true if they are covariant.  */
+
+static bool
+covariant_with_builtin_type_p (Type *t1, Type *t2)
+{
+  /* Check whether the declared function matches the built-in.  */
+  if (same_type_p (t1, t2) || t1->covariant (t2) == Covariant::yes)
+    return true;
+
+  /* May not be covariant because of D attributes applied on t1.
+     Strip them all off and compare again.  */
+  TypeFunction *tf1 = t1->isTypeFunction ();
+  TypeFunction *tf2 = t2->isTypeFunction ();
+
+  /* Check for obvious reasons why types may be distinct.  */
+  if (tf1 == NULL || tf2 == NULL
+      || tf1->isref () != tf2->isref ()
+      || tf1->parameterList.varargs != tf2->parameterList.varargs
+      || tf1->parameterList.length () != tf2->parameterList.length ())
+    return false;
+
+  /* Check return type and each parameter type for mismatch.  */
+  if (!matches_builtin_type (tf1->next, tf2->next))
+    return false;
+
+  const size_t nparams = tf1->parameterList.length ();
+  for (size_t i = 0; i < nparams; i++)
+    {
+      Parameter *fparam1 = tf1->parameterList[i];
+      Parameter *fparam2 = tf2->parameterList[i];
+
+      if (fparam1->isReference () != fparam2->isReference ()
+	  || fparam1->isLazy () != fparam2->isLazy ())
+	return false;
+
+      if (!matches_builtin_type (fparam1->type, fparam2->type))
+	return false;
+    }
+
+  return true;
+}
+
 /* Search for any `extern(C)' functions that match any known GCC library builtin
    function in D and override its internal back-end symbol.  */
 
@@ -694,23 +775,46 @@  maybe_set_builtin_1 (Dsymbol *d)
 	    }
 	}
     }
-  else if (fd && !fd->fbody)
+  else if (fd && !fd->fbody && fd->resolvedLinkage () == LINK::c)
     {
-      tree t;
+      tree ident = get_identifier (fd->ident->toChars ());
+      tree decl = IDENTIFIER_DECL_TREE (ident);
 
-      for (size_t i = 0; vec_safe_iterate (gcc_builtins_libfuncs, i, &t); ++i)
+      if (decl && TREE_CODE (decl) == FUNCTION_DECL
+	  && DECL_ASSEMBLER_NAME_SET_P (decl)
+	  && fndecl_built_in_p (decl, BUILT_IN_NORMAL))
 	{
-	  gcc_assert (DECL_ASSEMBLER_NAME_SET_P (t));
-
-	  const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (t));
-	  if (fd->ident != Identifier::idPool (name))
-	    continue;
-
 	  /* Found a match, tell the frontend this is a builtin.  */
-	  DECL_LANG_SPECIFIC (t) = build_lang_decl (fd);
-	  fd->csym = t;
+	  DECL_LANG_SPECIFIC (decl) = build_lang_decl (fd);
+	  fd->csym = decl;
 	  fd->builtin = BUILTIN::gcc;
-	  return;
+
+	  /* Copy front-end attributes to the builtin.  */
+	  apply_user_attributes (fd, fd->csym);
+
+	  /* Function has `pragma(mangle)' specified, override its name.  */
+	  if (fd->mangleOverride.length)
+	    {
+	      tree mangle =
+		get_identifier_with_length (fd->mangleOverride.ptr,
+					    fd->mangleOverride.length);
+	      const char *asmname = IDENTIFIER_POINTER (mangle);
+	      set_builtin_user_assembler_name (decl, asmname);
+	    }
+
+	  /* Warn when return and argument types of the user defined function is
+	     not covariant with the built-in function type.  */
+	  if (Type *type = build_frontend_type (TREE_TYPE (decl)))
+	    {
+	      if (!covariant_with_builtin_type_p (fd->type, type))
+		{
+		  warning_at (make_location_t (fd->loc),
+			      OPT_Wbuiltin_declaration_mismatch,
+			      "conflicting types for built-in function %qs; "
+			      "expected %qs",
+			      fd->toChars (), type->toChars ());
+		}
+	    }
 	}
     }
 }
@@ -1221,7 +1325,11 @@  tree
 d_builtin_function (tree decl)
 {
   if (!flag_no_builtin && DECL_ASSEMBLER_NAME_SET_P (decl))
-    vec_safe_push (gcc_builtins_libfuncs, decl);
+    {
+      /* Associate the assembler identifier with the built-in.  */
+      tree ident = DECL_ASSEMBLER_NAME (decl);
+      IDENTIFIER_DECL_TREE (ident) = decl;
+    }
 
   vec_safe_push (gcc_builtins_functions, decl);
   return decl;
diff --git a/gcc/d/d-compiler.cc b/gcc/d/d-compiler.cc
index 434bd251df9..881f3863d2a 100644
--- a/gcc/d/d-compiler.cc
+++ b/gcc/d/d-compiler.cc
@@ -119,31 +119,37 @@  Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
    Modules we look out for are:
     - object: For D runtime type information.
     - gcc.builtins: For all gcc builtins.
-    - core.stdc.*: For all gcc library builtins.  */
+    - all other modules for extern(C) gcc library builtins.  */
 
 void
 Compiler::onParseModule (Module *m)
 {
   ModuleDeclaration *md = m->md;
 
-  if (!md || !md->id|| md->packages.length == 0)
+  if (md && md->id)
     {
-      Identifier *id = (md && md->id) ? md->id : m->ident;
-      if (!strcmp (id->toChars (), "object"))
-	create_tinfo_types (m);
-    }
-  else if (md->packages.length == 1)
-    {
-      if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
-	  && !strcmp (md->id->toChars (), "builtins"))
-	d_build_builtins_module (m);
-    }
-  else if (md->packages.length == 2)
-    {
-      if (!strcmp (md->packages.ptr[0]->toChars (), "core")
-	  && !strcmp (md->packages.ptr[1]->toChars (), "stdc"))
-	d_add_builtin_module (m);
+      if (md->packages.length == 0)
+    	{
+    	  Identifier *id = (md && md->id) ? md->id : m->ident;
+    	  if (!strcmp (id->toChars (), "object"))
+	    {
+	      create_tinfo_types (m);
+	      return;
+	    }
+	}
+      else if (md->packages.length == 1)
+    	{
+    	  if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
+    	      && !strcmp (md->id->toChars (), "builtins"))
+    	    {
+    	      d_build_builtins_module (m);
+    	      return;
+    	    }
+	}
     }
+
+  if (!flag_no_builtin)
+    d_add_builtin_module (m);
 }
 
 /* A callback function that is called once an imported module is parsed.
diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt
index 954eb9a22f1..bd9c61d540d 100644
--- a/gcc/d/lang.opt
+++ b/gcc/d/lang.opt
@@ -118,6 +118,10 @@  Wno-alloca-larger-than
 D
 ; Documented in C
 
+Wbuiltin-declaration-mismatch
+D
+; Documented in C
+
 Wcast-result
 D Warning Var(warn_cast_result) LangEnabledBy(D, Wextra)
 Warn about casts that will produce a null result.
diff --git a/gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d b/gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d
new file mode 100644
index 00000000000..53406474a85
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d
@@ -0,0 +1,37 @@ 
+// { dg-do compile }
+// { dg-options "-Wbuiltin-declaration-mismatch" }
+
+extern(C):
+
+// Mismatched parameter lengths
+double tan();             // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Mismatched variadic arguments
+int printf(const(char)*); // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Mismatched return type
+void puts(char*);         // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Mismatched return storage class
+ref int isalnum(int);     // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Mismatched parameter type
+double sin(long);         // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Mismatched parameter storage class
+double frexp(double, lazy int*); // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+double log(ref double);          // { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" }
+
+// Verify that storage classes don't affect covariance matching
+@trusted nothrow @nogc pure double fabs(double);
+
+// Verify inout is allowed instead of const
+inout(char)* strstr(return scope inout(char)*, scope const char*) pure;
+
+// Verify that FILE* is allowed as it is implicitly convertable to void*
+struct _IO_FILE{}
+alias FILE = shared(_IO_FILE);
+int fprintf(FILE*, scope const char*, scope const ...);
+
+// Verify integral types with same size are treated as if equivalent
+int putchar(dchar);
diff --git a/gcc/testsuite/gdc.dg/builtins.d b/gcc/testsuite/gdc.dg/builtins.d
new file mode 100644
index 00000000000..21e3a1ee0ac
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/builtins.d
@@ -0,0 +1,17 @@ 
+// { dg-do compile }
+// { dg-options "-fdump-tree-original" }
+
+// { dg-final { scan-tree-dump " __builtin_sqrt " "original" } }
+extern(C) double sqrt(double);
+double test_sqrt(double a) { return sqrt(a); }
+
+// { dg-final { scan-tree-dump-not " __builtin_tan " "original" } }
+pragma(mangle, "tan")
+extern(C) double libc_tan(double);
+double test_tan(double a) { return libc_tan(a); }
+
+// { dg-final { scan-tree-dump " __builtin_malloc " "original" } }
+// { dg-final { scan-assembler "mangle_override" } }
+pragma(mangle, "mangle_override")
+extern(C) void *malloc(size_t);
+void* test_malloc(size_t a) { return malloc(a); }