aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-06-11 12:40:00 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2022-06-13 16:37:13 +0200
commit77718f38f896191e39b1e14c66ed990f0fff391b (patch)
tree12501410623ab6c27871a8584af8d38f9b7d0928
parentf8baf4004ef965ce7a9edf6d2f5eb99adb15803a (diff)
downloadgcc-77718f38f896191e39b1e14c66ed990f0fff391b.zip
gcc-77718f38f896191e39b1e14c66ed990f0fff391b.tar.gz
gcc-77718f38f896191e39b1e14c66ed990f0fff391b.tar.bz2
d: Match function declarations of gcc built-ins from any module.
Declarations of recognised gcc built-in functions are now matched 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. 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.
-rw-r--r--gcc/d/d-builtins.cc136
-rw-r--r--gcc/d/d-compiler.cc40
-rw-r--r--gcc/d/lang.opt4
-rw-r--r--gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d37
-rw-r--r--gcc/testsuite/gdc.dg/builtins.d17
5 files changed, 203 insertions, 31 deletions
diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc
index cd9748c..c2ef0c8 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 434bd25..881f386 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 954eb9a..bd9c61d 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 0000000..5340647
--- /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 0000000..21e3a1e
--- /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); }