From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [IPv6:2001:67c:2050:0:465::202]) by sourceware.org (Postfix) with ESMTPS id 66A563851A9A for ; Mon, 13 Jun 2022 14:40:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 66A563851A9A Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4LMDkT6RGQz9snl; Mon, 13 Jun 2022 16:40:17 +0200 (CEST) From: Iain Buclaw To: gcc-patches@gcc.gnu.org Subject: [committed] d: Match function declarations of gcc built-ins from any module. Date: Mon, 13 Jun 2022 16:40:10 +0200 Message-Id: <20220613144010.1822566-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-12.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 13 Jun 2022 14:40:26 -0000 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 --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 *gcc_builtins_functions = NULL; -static GTY(()) vec *gcc_builtins_libfuncs = NULL; static GTY(()) vec *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); } -- 2.34.1