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.
This commit is contained in:
parent
f8baf4004e
commit
77718f38f8
5 changed files with 203 additions and 31 deletions
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
37
gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d
Normal file
37
gcc/testsuite/gdc.dg/Wbuiltin_declaration_mismatch.d
Normal file
|
@ -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);
|
17
gcc/testsuite/gdc.dg/builtins.d
Normal file
17
gcc/testsuite/gdc.dg/builtins.d
Normal file
|
@ -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); }
|
Loading…
Add table
Reference in a new issue