2573 lines
74 KiB
C++
2573 lines
74 KiB
C++
/* decl.cc -- Lower D frontend declarations to GCC trees.
|
|
Copyright (C) 2006-2022 Free Software Foundation, Inc.
|
|
|
|
GCC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
|
|
#include "dmd/aggregate.h"
|
|
#include "dmd/attrib.h"
|
|
#include "dmd/cond.h"
|
|
#include "dmd/ctfe.h"
|
|
#include "dmd/declaration.h"
|
|
#include "dmd/enum.h"
|
|
#include "dmd/errors.h"
|
|
#include "dmd/globals.h"
|
|
#include "dmd/hdrgen.h"
|
|
#include "dmd/identifier.h"
|
|
#include "dmd/import.h"
|
|
#include "dmd/init.h"
|
|
#include "dmd/mangle.h"
|
|
#include "dmd/module.h"
|
|
#include "dmd/nspace.h"
|
|
#include "dmd/target.h"
|
|
#include "dmd/template.h"
|
|
|
|
#include "tree.h"
|
|
#include "tree-iterator.h"
|
|
#include "fold-const.h"
|
|
#include "diagnostic.h"
|
|
#include "langhooks.h"
|
|
#include "target.h"
|
|
#include "common/common-target.h"
|
|
#include "cgraph.h"
|
|
#include "toplev.h"
|
|
#include "stringpool.h"
|
|
#include "varasm.h"
|
|
#include "stor-layout.h"
|
|
#include "attribs.h"
|
|
#include "function.h"
|
|
#include "debug.h"
|
|
#include "tree-pretty-print.h"
|
|
#include "tree-nested.h"
|
|
#include "alloc-pool.h"
|
|
#include "symbol-summary.h"
|
|
#include "symtab-thunks.h"
|
|
#include "gimple-expr.h"
|
|
|
|
#include "d-tree.h"
|
|
#include "d-target.h"
|
|
|
|
|
|
/* Return identifier for the external mangled name of DECL. */
|
|
|
|
const char *
|
|
d_mangle_decl (Dsymbol *decl)
|
|
{
|
|
if (decl->isFuncDeclaration ())
|
|
return mangleExact ((FuncDeclaration *) decl);
|
|
else
|
|
{
|
|
OutBuffer buf;
|
|
mangleToBuffer (decl, &buf);
|
|
return buf.extractChars ();
|
|
}
|
|
}
|
|
|
|
/* Generate a mangled identifier using NAME and SUFFIX, prefixed by the
|
|
assembler name for DECL. */
|
|
|
|
tree
|
|
mangle_internal_decl (Dsymbol *decl, const char *name, const char *suffix)
|
|
{
|
|
const char *prefix = d_mangle_decl (decl);
|
|
unsigned namelen = strlen (name);
|
|
unsigned buflen = (2 + strlen (prefix) + namelen + strlen (suffix)) * 2;
|
|
char *buf = (char *) alloca (buflen);
|
|
|
|
snprintf (buf, buflen, "_D%s%u%s%s", prefix, namelen, name, suffix);
|
|
tree ident = get_identifier (buf);
|
|
|
|
/* Symbol is not found in user code, but generate a readable name for it
|
|
anyway for debug and diagnostic reporting. */
|
|
snprintf (buf, buflen, "%s.%s", decl->toPrettyChars (), name);
|
|
IDENTIFIER_PRETTY_NAME (ident) = get_identifier (buf);
|
|
|
|
return ident;
|
|
}
|
|
|
|
/* Returns true if DECL is from the gcc.attributes module. */
|
|
|
|
static bool
|
|
gcc_attribute_p (Dsymbol *decl)
|
|
{
|
|
ModuleDeclaration *md = decl->getModule ()->md;
|
|
|
|
if (md && md->packages.length == 1)
|
|
{
|
|
if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
|
|
&& !strcmp (md->id->toChars (), "attributes"))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Implements the visitor interface to lower all Declaration AST classes
|
|
emitted from the D Front-end to GCC trees.
|
|
All visit methods accept one parameter D, which holds the frontend AST
|
|
of the declaration to compile. These also don't return any value, instead
|
|
generated code are appened to global_declarations or added to the
|
|
current_binding_level by d_pushdecl(). */
|
|
|
|
class DeclVisitor : public Visitor
|
|
{
|
|
using Visitor::visit;
|
|
|
|
/* If we're lowering the body of a version(unittest) condition. */
|
|
bool in_version_unittest_;
|
|
|
|
public:
|
|
DeclVisitor (void)
|
|
{
|
|
this->in_version_unittest_ = false;
|
|
}
|
|
|
|
/* Helper for generating code for the dsymbol AST class D.
|
|
Sets up the location of the symbol before lowering. */
|
|
|
|
void build_dsymbol (Dsymbol *d)
|
|
{
|
|
location_t saved_location = input_location;
|
|
input_location = make_location_t (d->loc);
|
|
d->accept (this);
|
|
input_location = saved_location;
|
|
}
|
|
|
|
/* This should be overridden by each declaration class. */
|
|
|
|
void visit (Dsymbol *) final override
|
|
{
|
|
}
|
|
|
|
/* Compile a D module, and all members of it. */
|
|
|
|
void visit (Module *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
build_module_tree (d);
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Write the imported symbol to debug. */
|
|
|
|
void visit (Import *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
/* Implements import declarations by telling the debug back-end we are
|
|
importing the NAMESPACE_DECL of the module or IMPORTED_DECL of the
|
|
declaration into the current lexical scope CONTEXT. NAME is set if
|
|
this is a renamed import. */
|
|
if (d->isstatic)
|
|
return;
|
|
|
|
/* Get the context of this import, this should never be null. */
|
|
tree context = d_module_context ();
|
|
|
|
if (d->ident == NULL)
|
|
{
|
|
/* Importing declaration list. */
|
|
for (size_t i = 0; i < d->names.length; i++)
|
|
{
|
|
AliasDeclaration *aliasdecl = d->aliasdecls[i];
|
|
tree decl = build_import_decl (aliasdecl);
|
|
|
|
/* Skip over unhandled imports. */
|
|
if (decl == NULL_TREE)
|
|
continue;
|
|
|
|
Identifier *alias = d->aliases[i];
|
|
tree name = (alias != NULL)
|
|
? get_identifier (alias->toChars ()) : NULL_TREE;
|
|
|
|
debug_hooks->imported_module_or_decl (decl, name, context,
|
|
false, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Importing the entire module. */
|
|
tree decl = build_import_decl (d->mod);
|
|
|
|
tree name = (d->aliasId != NULL)
|
|
? get_identifier (d->aliasId->toChars ()) : NULL_TREE;
|
|
|
|
debug_hooks->imported_module_or_decl (decl, name, context,
|
|
false, false);
|
|
}
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Expand any local variables found in tuples. */
|
|
|
|
void visit (TupleDeclaration *d) final override
|
|
{
|
|
for (size_t i = 0; i < d->objects->length; i++)
|
|
{
|
|
RootObject *o = (*d->objects)[i];
|
|
if (o->dyncast () == DYNCAST_EXPRESSION)
|
|
{
|
|
VarExp *ve = ((Expression *) o)->isVarExp ();
|
|
if (ve)
|
|
this->build_dsymbol (ve->var);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Walk over all declarations in the attribute scope. */
|
|
|
|
void visit (AttribDeclaration *d) final override
|
|
{
|
|
Dsymbols *ds = d->include (NULL);
|
|
|
|
if (!ds)
|
|
return;
|
|
|
|
for (size_t i = 0; i < ds->length; i++)
|
|
this->build_dsymbol ((*ds)[i]);
|
|
}
|
|
|
|
/* Pragmas are a way to pass special information to the compiler and to add
|
|
vendor specific extensions to D. */
|
|
|
|
void visit (PragmaDeclaration *d) final override
|
|
{
|
|
if (d->ident == Identifier::idPool ("lib")
|
|
|| d->ident == Identifier::idPool ("startaddress"))
|
|
{
|
|
if (!global.params.ignoreUnsupportedPragmas)
|
|
{
|
|
warning_at (make_location_t (d->loc), OPT_Wunknown_pragmas,
|
|
"pragma(%s) not implemented", d->ident->toChars ());
|
|
}
|
|
}
|
|
|
|
visit ((AttribDeclaration *) d);
|
|
}
|
|
|
|
/* Conditional compilation is the process of selecting which code to compile
|
|
and which code to not compile. Look for version conditions that may */
|
|
|
|
void visit (ConditionalDeclaration *d) final override
|
|
{
|
|
bool old_condition = this->in_version_unittest_;
|
|
|
|
if (global.params.useUnitTests)
|
|
{
|
|
VersionCondition *vc = d->condition->isVersionCondition ();
|
|
if (vc && vc->ident == Identifier::idPool ("unittest"))
|
|
this->in_version_unittest_ = true;
|
|
}
|
|
|
|
visit ((AttribDeclaration *) d);
|
|
|
|
this->in_version_unittest_ = old_condition;
|
|
}
|
|
|
|
/* Walk over all members in the namespace scope. */
|
|
|
|
void visit (Nspace *d) final override
|
|
{
|
|
if (isError (d) || !d->members)
|
|
return;
|
|
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
}
|
|
|
|
/* Templates are D's approach to generic programming. They have no members
|
|
that can be emitted, however if the template is nested and used as a
|
|
voldemort type, then it's members must be compiled before the parent
|
|
function finishes. */
|
|
|
|
void visit (TemplateDeclaration *d) final override
|
|
{
|
|
/* Type cannot be directly named outside of the scope it's declared in, so
|
|
the only way it can be escaped is if the function has auto return. */
|
|
FuncDeclaration *fd = d_function_chain ? d_function_chain->function : NULL;
|
|
|
|
if (!fd || !fd->isAuto ())
|
|
return;
|
|
|
|
/* Check if the function returns an instantiated type that may contain
|
|
nested members. Only applies to classes or structs. */
|
|
Type *tb = fd->type->nextOf ()->baseElemOf ();
|
|
|
|
while (tb->ty == TY::Tarray || tb->ty == TY::Tpointer)
|
|
tb = tb->nextOf ()->baseElemOf ();
|
|
|
|
TemplateInstance *ti = NULL;
|
|
|
|
if (tb->ty == TY::Tstruct)
|
|
ti = tb->isTypeStruct ()->sym->isInstantiated ();
|
|
else if (tb->ty == TY::Tclass)
|
|
ti = tb->isTypeClass ()->sym->isInstantiated ();
|
|
|
|
/* Return type is instantiated from this template declaration, walk over
|
|
all members of the instance. */
|
|
if (ti && ti->tempdecl == d)
|
|
this->build_dsymbol (ti);
|
|
}
|
|
|
|
/* Walk over all members in the instantiated template. */
|
|
|
|
void visit (TemplateInstance *d) final override
|
|
{
|
|
if (isError (d)|| !d->members)
|
|
return;
|
|
|
|
if (!d->needsCodegen ())
|
|
return;
|
|
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
}
|
|
|
|
/* Walk over all members in the mixin template scope. */
|
|
|
|
void visit (TemplateMixin *d) final override
|
|
{
|
|
if (isError (d)|| !d->members)
|
|
return;
|
|
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
}
|
|
|
|
/* Write out compiler generated TypeInfo, initializer and functions for the
|
|
given struct declaration, walking over all static members. */
|
|
|
|
void visit (StructDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (d->type->ty == TY::Terror)
|
|
{
|
|
error_at (make_location_t (d->loc),
|
|
"had semantic errors when compiling");
|
|
return;
|
|
}
|
|
|
|
/* Don't emit any symbols from gcc.attributes module. */
|
|
if (gcc_attribute_p (d))
|
|
return;
|
|
|
|
/* Add this decl to the current binding level. */
|
|
tree ctype = build_ctype (d->type);
|
|
if (TYPE_NAME (ctype))
|
|
d_pushdecl (TYPE_NAME (ctype));
|
|
|
|
/* Anonymous structs/unions only exist as part of others,
|
|
do not output forward referenced structs. */
|
|
if (d->isAnonymous () || !d->members)
|
|
return;
|
|
|
|
/* Generate TypeInfo. */
|
|
if (have_typeinfo_p (Type::dtypeinfo))
|
|
create_typeinfo (d->type, NULL);
|
|
|
|
/* Generate static initializer. */
|
|
tree sinit = aggregate_initializer_decl (d);
|
|
DECL_INITIAL (sinit) = layout_struct_initializer (d);
|
|
d_finish_decl (sinit);
|
|
|
|
/* Put out the members. There might be static constructors in the members
|
|
list, and they cannot be put in separate object files. */
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
|
|
/* Put out xopEquals, xopCmp and xopHash. */
|
|
if (d->xeq && d->xeq != d->xerreq)
|
|
this->build_dsymbol (d->xeq);
|
|
|
|
if (d->xcmp && d->xcmp != d->xerrcmp)
|
|
this->build_dsymbol (d->xcmp);
|
|
|
|
if (d->xhash)
|
|
this->build_dsymbol (d->xhash);
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Finish semantic analysis of functions in vtbl for class CD. */
|
|
|
|
bool finish_vtable (ClassDeclaration *d)
|
|
{
|
|
bool has_errors = false;
|
|
|
|
/* Finish semantic analysis of functions in vtbl[]. */
|
|
for (size_t i = d->vtblOffset (); i < d->vtbl.length; i++)
|
|
{
|
|
FuncDeclaration *fd = d->vtbl[i]->isFuncDeclaration ();
|
|
|
|
if (!fd || (!fd->fbody && d->isAbstract ()))
|
|
continue;
|
|
|
|
/* Ensure function has a return value. */
|
|
if (!fd->functionSemantic ())
|
|
has_errors = true;
|
|
|
|
/* No name hiding to check for. */
|
|
if (!d->isFuncHidden (fd) || fd->isFuture ())
|
|
continue;
|
|
|
|
/* The function fd is hidden from the view of the class.
|
|
If it overlaps with any function in the vtbl[], then
|
|
issue an error. */
|
|
for (size_t j = 1; j < d->vtbl.length; j++)
|
|
{
|
|
if (j == i)
|
|
continue;
|
|
|
|
FuncDeclaration *fd2 = d->vtbl[j]->isFuncDeclaration ();
|
|
if (!fd2->ident->equals (fd->ident))
|
|
continue;
|
|
|
|
/* The function is marked as @__future, a deprecation has
|
|
already been given by the frontend. */
|
|
if (fd2->isFuture ())
|
|
continue;
|
|
|
|
if (fd->leastAsSpecialized (fd2) != MATCH::nomatch
|
|
|| fd2->leastAsSpecialized (fd) != MATCH::nomatch)
|
|
{
|
|
error_at (make_location_t (fd->loc), "use of %qs",
|
|
fd->toPrettyChars ());
|
|
inform (make_location_t (fd2->loc), "is hidden by %qs",
|
|
fd2->toPrettyChars ());
|
|
inform (make_location_t (d->loc),
|
|
"use %<alias %s = %s.%s;%> to introduce base class "
|
|
"overload set", fd->toChars (),
|
|
fd->parent->toChars (), fd->toChars ());
|
|
has_errors = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return !has_errors;
|
|
}
|
|
|
|
/* Write out compiler generated TypeInfo, initializer and vtables for the
|
|
given class declaration, walking over all static members. */
|
|
|
|
void visit (ClassDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (d->type->ty == TY::Terror)
|
|
{
|
|
error_at (make_location_t (d->loc),
|
|
"had semantic errors when compiling");
|
|
return;
|
|
}
|
|
|
|
/* Add this decl to the current binding level. */
|
|
tree ctype = TREE_TYPE (build_ctype (d->type));
|
|
if (TYPE_NAME (ctype))
|
|
d_pushdecl (TYPE_NAME (ctype));
|
|
|
|
if (!d->members)
|
|
return;
|
|
|
|
/* Put out the members. */
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
|
|
/* If something goes wrong during final semantic pass, don't bother with
|
|
the rest as we may have incomplete info. */
|
|
if (!this->finish_vtable (d))
|
|
return;
|
|
|
|
/* Generate C symbols. */
|
|
d->csym = get_classinfo_decl (d);
|
|
Dsymbol *vtblsym = d->vtblSymbol ();
|
|
vtblsym->csym = get_vtable_decl (d);
|
|
tree sinit = aggregate_initializer_decl (d);
|
|
|
|
/* Generate static initializer. */
|
|
DECL_INITIAL (sinit) = layout_class_initializer (d);
|
|
d_finish_decl (sinit);
|
|
|
|
/* Put out the TypeInfo. */
|
|
if (have_typeinfo_p (Type::dtypeinfo))
|
|
create_typeinfo (d->type, NULL);
|
|
|
|
DECL_INITIAL (d->csym) = layout_classinfo (d);
|
|
d_finish_decl (d->csym);
|
|
|
|
/* Put out the vtbl[]. */
|
|
vec <constructor_elt, va_gc> *elms = NULL;
|
|
|
|
/* First entry is ClassInfo reference. */
|
|
if (d->vtblOffset ())
|
|
CONSTRUCTOR_APPEND_ELT (elms, size_zero_node, build_address (d->csym));
|
|
|
|
for (size_t i = d->vtblOffset (); i < d->vtbl.length; i++)
|
|
{
|
|
FuncDeclaration *fd = d->vtbl[i]->isFuncDeclaration ();
|
|
|
|
if (fd && (fd->fbody || !d->isAbstract ()))
|
|
{
|
|
CONSTRUCTOR_APPEND_ELT (elms, size_int (i),
|
|
build_address (get_symbol_decl (fd)));
|
|
}
|
|
}
|
|
|
|
DECL_INITIAL (vtblsym->csym)
|
|
= build_constructor (TREE_TYPE (vtblsym->csym), elms);
|
|
d_finish_decl (vtblsym->csym);
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Write out compiler generated TypeInfo and vtables for the given interface
|
|
declaration, walking over all static members. */
|
|
|
|
void visit (InterfaceDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (d->type->ty == TY::Terror)
|
|
{
|
|
error_at (make_location_t (d->loc),
|
|
"had semantic errors when compiling");
|
|
return;
|
|
}
|
|
|
|
/* Add this decl to the current binding level. */
|
|
tree ctype = TREE_TYPE (build_ctype (d->type));
|
|
if (TYPE_NAME (ctype))
|
|
d_pushdecl (TYPE_NAME (ctype));
|
|
|
|
if (!d->members)
|
|
return;
|
|
|
|
/* Put out the members. */
|
|
for (size_t i = 0; i < d->members->length; i++)
|
|
this->build_dsymbol ((*d->members)[i]);
|
|
|
|
/* Generate C symbols. */
|
|
d->csym = get_classinfo_decl (d);
|
|
|
|
/* Put out the TypeInfo. */
|
|
if (have_typeinfo_p (Type::dtypeinfo))
|
|
{
|
|
create_typeinfo (d->type, NULL);
|
|
this->build_dsymbol (d->type->vtinfo);
|
|
}
|
|
|
|
DECL_INITIAL (d->csym) = layout_classinfo (d);
|
|
d_finish_decl (d->csym);
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Write out compiler generated TypeInfo and initializer for the given
|
|
enum declaration. */
|
|
|
|
void visit (EnumDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (d->errors || d->type->ty == TY::Terror)
|
|
{
|
|
error_at (make_location_t (d->loc),
|
|
"had semantic errors when compiling");
|
|
return;
|
|
}
|
|
|
|
/* Add this decl to the current binding level. */
|
|
tree ctype = build_ctype (d->type);
|
|
if (TREE_CODE (ctype) == ENUMERAL_TYPE && TYPE_NAME (ctype))
|
|
d_pushdecl (TYPE_NAME (ctype));
|
|
|
|
if (d->isAnonymous ())
|
|
return;
|
|
|
|
/* Generate TypeInfo. */
|
|
if (have_typeinfo_p (Type::dtypeinfo))
|
|
create_typeinfo (d->type, NULL);
|
|
|
|
TypeEnum *tc = d->type->isTypeEnum ();
|
|
if (tc->sym->members && !d->type->isZeroInit ())
|
|
{
|
|
/* Generate static initializer. */
|
|
d->sinit = enum_initializer_decl (d);
|
|
DECL_INITIAL (d->sinit) = build_expr (tc->sym->defaultval, true);
|
|
d_finish_decl (d->sinit);
|
|
}
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Finish up a variable declaration and push it into the current scope.
|
|
This can either be a static, local or manifest constant. */
|
|
|
|
void visit (VarDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (d->type->ty == TY::Terror)
|
|
{
|
|
error_at (make_location_t (d->loc),
|
|
"had semantic errors when compiling");
|
|
return;
|
|
}
|
|
|
|
/* Variables of type `noreturn` are just placeholders, and evaluate to
|
|
`assert(0)` if ever read. */
|
|
if (d->type->isTypeNoreturn ())
|
|
{
|
|
if (!d->isDataseg () && !d->isMember ()
|
|
&& d->_init && !d->_init->isVoidInitializer ())
|
|
{
|
|
/* Evaluate RHS for side effects first. */
|
|
Expression *ie = initializerToExpression (d->_init);
|
|
add_stmt (build_expr (ie));
|
|
|
|
Expression *e = d->type->defaultInitLiteral (d->loc);
|
|
add_stmt (build_expr (e));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (d->aliassym)
|
|
{
|
|
this->build_dsymbol (d->toAlias ());
|
|
return;
|
|
}
|
|
|
|
if (!d->canTakeAddressOf ())
|
|
{
|
|
/* Do not store variables we cannot take the address of,
|
|
but keep the values for purposes of debugging. */
|
|
if (!d->type->isscalar ())
|
|
{
|
|
tree decl = get_symbol_decl (d);
|
|
d_pushdecl (decl);
|
|
rest_of_decl_compilation (decl, 1, 0);
|
|
}
|
|
}
|
|
else if (d->isDataseg ())
|
|
{
|
|
tree decl = get_symbol_decl (d);
|
|
|
|
/* Only need to build the VAR_DECL for extern declarations. */
|
|
if (d->storage_class & STCextern)
|
|
return;
|
|
|
|
/* Duplicated VarDeclarations map to the same symbol. Check if this
|
|
is the one declaration which will be emitted. */
|
|
tree ident = DECL_ASSEMBLER_NAME (decl);
|
|
if (IDENTIFIER_DSYMBOL (ident) && IDENTIFIER_DSYMBOL (ident) != d)
|
|
return;
|
|
|
|
/* How big a symbol can be should depend on back-end. */
|
|
tree size = build_integer_cst (d->type->size (d->loc),
|
|
build_ctype (Type::tsize_t));
|
|
if (!valid_constant_size_p (size))
|
|
{
|
|
error_at (make_location_t (d->loc), "size is too large");
|
|
return;
|
|
}
|
|
|
|
if (d->_init)
|
|
{
|
|
/* Use the explicit initializer, this includes `void`. */
|
|
if (!d->_init->isVoidInitializer ())
|
|
{
|
|
Expression *e = initializerToExpression (d->_init, d->type);
|
|
DECL_INITIAL (decl) = build_expr (e, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use default initializer for the type. */
|
|
if (TypeStruct *ts = d->type->isTypeStruct ())
|
|
DECL_INITIAL (decl) = layout_struct_initializer (ts->sym);
|
|
else
|
|
{
|
|
Expression *e = d->type->defaultInitLiteral (d->loc);
|
|
DECL_INITIAL (decl) = build_expr (e, true);
|
|
}
|
|
}
|
|
|
|
/* Frontend should have already caught this. */
|
|
gcc_assert (!integer_zerop (size)
|
|
|| d->type->toBasetype ()->isTypeSArray ());
|
|
|
|
d_finish_decl (decl);
|
|
|
|
/* Maybe record the var against the current module. */
|
|
register_module_decl (d);
|
|
}
|
|
else if (!d->isDataseg () && !d->isMember ())
|
|
{
|
|
/* This is needed for VarDeclarations in mixins that are to be local
|
|
variables of a function. Otherwise, it would be enough to make
|
|
a check for isVarDeclaration() in DeclarationExp codegen. */
|
|
declare_local_var (d);
|
|
|
|
if (d->_init && !d->_init->isVoidInitializer ())
|
|
{
|
|
tree decl = get_symbol_decl (d);
|
|
|
|
ExpInitializer *vinit = d->_init->isExpInitializer ();
|
|
Expression *ie = initializerToExpression (vinit);
|
|
tree exp = build_expr (ie);
|
|
|
|
/* Maybe put variable on list of things needing destruction. */
|
|
if (d->needsScopeDtor ())
|
|
{
|
|
vec_safe_push (d_function_chain->vars_in_scope, decl);
|
|
/* Force a TARGET_EXPR to add the corresponding cleanup. */
|
|
exp = force_target_expr (compound_expr (exp, decl));
|
|
TARGET_EXPR_CLEANUP (exp) = build_expr (d->edtor);
|
|
}
|
|
|
|
add_stmt (exp);
|
|
}
|
|
}
|
|
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Generate and compile a static TypeInfo declaration, but only if it is
|
|
needed in the current compilation. */
|
|
|
|
void visit (TypeInfoDeclaration *d) final override
|
|
{
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
if (speculative_type_p (d->tinfo))
|
|
return;
|
|
|
|
tree t = get_typeinfo_decl (d);
|
|
DECL_INITIAL (t) = layout_typeinfo (d);
|
|
d_finish_decl (t);
|
|
d->semanticRun = PASS::obj;
|
|
}
|
|
|
|
/* Finish up a function declaration and compile it all the way
|
|
down to assembler language output. */
|
|
|
|
void visit (FuncDeclaration *d) final override
|
|
{
|
|
/* Already generated the function. */
|
|
if (d->semanticRun >= PASS::obj)
|
|
return;
|
|
|
|
/* Don't emit any symbols from gcc.attributes module. */
|
|
if (gcc_attribute_p (d))
|
|
return;
|
|
|
|
/* Not emitting unittest functions. */
|
|
if (!global.params.useUnitTests && d->isUnitTestDeclaration ())
|
|
return;
|
|
|
|
/* Check if any errors occurred when running semantic. */
|
|
if (TypeFunction *tf = d->type->isTypeFunction ())
|
|
{
|
|
if (tf->next == NULL || tf->next->ty == TY::Terror)
|
|
return;
|
|
}
|
|
|
|
if (d->hasSemantic3Errors ())
|
|
return;
|
|
|
|
if (d->isNested ())
|
|
{
|
|
FuncDeclaration *fdp = d;
|
|
while (fdp && fdp->isNested ())
|
|
{
|
|
fdp = fdp->toParent2 ()->isFuncDeclaration ();
|
|
|
|
if (fdp == NULL)
|
|
break;
|
|
|
|
/* Parent failed to compile, but errors were gagged. */
|
|
if (fdp->hasSemantic3Errors ())
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Ensure all semantic passes have run. */
|
|
if (d->semanticRun < PASS::semantic3)
|
|
{
|
|
d->functionSemantic3 ();
|
|
Module::runDeferredSemantic3 ();
|
|
}
|
|
|
|
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);
|
|
tree ident = DECL_ASSEMBLER_NAME (fndecl);
|
|
if (IDENTIFIER_DSYMBOL (ident) && IDENTIFIER_DSYMBOL (ident) != d)
|
|
return;
|
|
|
|
if (!d->fbody)
|
|
{
|
|
rest_of_decl_compilation (fndecl, 1, 0);
|
|
return;
|
|
}
|
|
|
|
if (global.params.verbose)
|
|
message ("function %s", d->toPrettyChars ());
|
|
|
|
tree old_context = start_function (d);
|
|
|
|
tree parm_decl = NULL_TREE;
|
|
tree param_list = NULL_TREE;
|
|
|
|
/* Special arguments... */
|
|
|
|
/* `this' parameter:
|
|
For nested functions, D still generates a vthis, but it
|
|
should not be referenced in any expression. */
|
|
if (d->vthis)
|
|
{
|
|
parm_decl = get_symbol_decl (d->vthis);
|
|
DECL_ARTIFICIAL (parm_decl) = 1;
|
|
TREE_READONLY (parm_decl) = 1;
|
|
|
|
if (d->vthis->type == Type::tvoidptr)
|
|
{
|
|
/* Replace generic pointer with back-end closure type
|
|
(this wins for gdb). */
|
|
tree frame_type = FRAMEINFO_TYPE (get_frameinfo (d));
|
|
gcc_assert (frame_type != NULL_TREE);
|
|
TREE_TYPE (parm_decl) = build_pointer_type (frame_type);
|
|
}
|
|
|
|
param_list = chainon (param_list, parm_decl);
|
|
d_function_chain->static_chain = parm_decl;
|
|
}
|
|
|
|
/* _arguments parameter. */
|
|
if (d->v_arguments)
|
|
{
|
|
parm_decl = get_symbol_decl (d->v_arguments);
|
|
param_list = chainon (param_list, parm_decl);
|
|
}
|
|
|
|
/* formal function parameters. */
|
|
const size_t n_parameters = d->parameters ? d->parameters->length : 0;
|
|
|
|
for (size_t i = 0; i < n_parameters; i++)
|
|
{
|
|
VarDeclaration *param = (*d->parameters)[i];
|
|
|
|
parm_decl = get_symbol_decl (param);
|
|
|
|
/* Type `noreturn` is a terminator, as no other arguments can possibly
|
|
be evaluated after it. */
|
|
if (TREE_TYPE (parm_decl) == noreturn_type_node)
|
|
break;
|
|
|
|
/* Chain them in the correct order. */
|
|
param_list = chainon (param_list, parm_decl);
|
|
}
|
|
|
|
DECL_ARGUMENTS (fndecl) = param_list;
|
|
DECL_IN_UNITTEST_CONDITION_P (fndecl) = this->in_version_unittest_;
|
|
rest_of_decl_compilation (fndecl, 1, 0);
|
|
|
|
/* If this is a member function that nested (possibly indirectly) in another
|
|
function, construct an expession for this member function's static chain
|
|
by going through parent link of nested classes. */
|
|
if (d->isThis ())
|
|
{
|
|
AggregateDeclaration *ad = d->isThis ();
|
|
tree this_tree = get_symbol_decl (d->vthis);
|
|
|
|
while (ad->isNested ())
|
|
{
|
|
Dsymbol *pd = ad->toParent2 ();
|
|
tree vthis_field = get_symbol_decl (ad->vthis);
|
|
this_tree = component_ref (build_deref (this_tree), vthis_field);
|
|
|
|
ad = pd->isAggregateDeclaration ();
|
|
if (ad == NULL)
|
|
{
|
|
cfun->language->static_chain = this_tree;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Named return value optimisation support for D.
|
|
Implemented by overriding all the RETURN_EXPRs and replacing all
|
|
occurrences of VAR with the RESULT_DECL for the function.
|
|
This is only worth doing for functions that can return in memory. */
|
|
tree resdecl = DECL_RESULT (fndecl);
|
|
|
|
if (TREE_ADDRESSABLE (TREE_TYPE (resdecl))
|
|
|| aggregate_value_p (TREE_TYPE (resdecl), fndecl))
|
|
{
|
|
/* Return non-trivial structs by invisible reference. */
|
|
if (TREE_ADDRESSABLE (TREE_TYPE (resdecl)))
|
|
{
|
|
TREE_TYPE (resdecl) = build_reference_type (TREE_TYPE (resdecl));
|
|
DECL_BY_REFERENCE (resdecl) = 1;
|
|
TREE_ADDRESSABLE (resdecl) = 0;
|
|
relayout_decl (resdecl);
|
|
d->shidden = build_deref (resdecl);
|
|
}
|
|
else
|
|
d->shidden = resdecl;
|
|
|
|
if (d->isNRVO () && d->nrvo_var)
|
|
{
|
|
tree var = get_symbol_decl (d->nrvo_var);
|
|
|
|
/* Copy name from VAR to RESULT. */
|
|
DECL_NAME (resdecl) = DECL_NAME (var);
|
|
/* Don't forget that we take its address. */
|
|
TREE_ADDRESSABLE (var) = 1;
|
|
|
|
SET_DECL_VALUE_EXPR (var, resdecl);
|
|
DECL_HAS_VALUE_EXPR_P (var) = 1;
|
|
SET_DECL_LANG_NRVO (var, d->shidden);
|
|
}
|
|
}
|
|
|
|
/* May change cfun->static_chain. */
|
|
build_closure (d);
|
|
|
|
if (d->vresult)
|
|
declare_local_var (d->vresult);
|
|
|
|
if (d->v_argptr)
|
|
push_stmt_list ();
|
|
|
|
build_function_body (d);
|
|
|
|
/* Initialize the _argptr variable. */
|
|
if (d->v_argptr)
|
|
{
|
|
tree body = pop_stmt_list ();
|
|
tree var = get_decl_tree (d->v_argptr);
|
|
var = build_address (var);
|
|
|
|
tree init = build_call_expr (builtin_decl_explicit (BUILT_IN_VA_START),
|
|
2, var, parm_decl);
|
|
declare_local_var (d->v_argptr);
|
|
add_stmt (init);
|
|
|
|
tree cleanup = build_call_expr (builtin_decl_explicit (BUILT_IN_VA_END),
|
|
1, var);
|
|
add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, body, cleanup));
|
|
}
|
|
|
|
finish_function (old_context);
|
|
|
|
/* Maybe record the function against the current module. */
|
|
register_module_decl (d);
|
|
}
|
|
};
|
|
|
|
/* Main entry point for the DeclVisitor interface to send
|
|
the Declaration AST class D to GCC back-end. */
|
|
|
|
void
|
|
build_decl_tree (Dsymbol *d)
|
|
{
|
|
location_t saved_location = input_location;
|
|
|
|
/* Set input location, empty DECL_SOURCE_FILE can crash debug generator. */
|
|
if (d->loc.filename)
|
|
input_location = make_location_t (d->loc);
|
|
else
|
|
input_location = make_location_t (Loc ("<no_file>", 1, 0));
|
|
|
|
DeclVisitor v = DeclVisitor ();
|
|
v.build_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 maybe_build_decl_tree (decl);
|
|
|
|
/* Deal with placeholder symbols immediately:
|
|
SymbolDeclaration is used as a shell around an initializer symbol. */
|
|
SymbolDeclaration *sd = decl->isSymbolDeclaration ();
|
|
if (sd)
|
|
{
|
|
decl->csym = aggregate_initializer_decl (sd->dsym);
|
|
return decl->csym;
|
|
}
|
|
|
|
/* Global static TypeInfo declaration. */
|
|
if (decl->isTypeInfoDeclaration ())
|
|
return get_typeinfo_decl ((TypeInfoDeclaration *) decl);
|
|
|
|
/* FuncAliasDeclaration is used to import functions from another scope. */
|
|
FuncAliasDeclaration *fad = decl->isFuncAliasDeclaration ();
|
|
if (fad)
|
|
{
|
|
decl->csym = get_symbol_decl (fad->funcalias);
|
|
return decl->csym;
|
|
}
|
|
|
|
/* It is possible for a field declaration symbol to be requested
|
|
before the parent type has been built. */
|
|
if (decl->isField ())
|
|
{
|
|
AggregateDeclaration *ad = decl->toParent ()->isAggregateDeclaration ();
|
|
gcc_assert (ad != NULL);
|
|
|
|
/* Finishing off the type should create the associated FIELD_DECL. */
|
|
build_ctype (ad->type);
|
|
gcc_assert (decl->csym != NULL);
|
|
|
|
return decl->csym;
|
|
}
|
|
|
|
/* Build the tree for the symbol. */
|
|
FuncDeclaration *fd = decl->isFuncDeclaration ();
|
|
if (fd)
|
|
{
|
|
/* Run full semantic on functions we need to know about. */
|
|
if (!fd->functionSemantic ())
|
|
{
|
|
decl->csym = error_mark_node;
|
|
return decl->csym;
|
|
}
|
|
|
|
decl->csym = build_decl (make_location_t (decl->loc), FUNCTION_DECL,
|
|
get_identifier (decl->ident->toChars ()),
|
|
NULL_TREE);
|
|
|
|
/* Set function type afterwards as there could be self references. */
|
|
TREE_TYPE (decl->csym) = build_ctype (fd->type);
|
|
|
|
/* Set DECL_INITIAL now if the function has a definition. */
|
|
if (fd->fbody)
|
|
DECL_INITIAL (decl->csym) = error_mark_node;
|
|
else
|
|
DECL_EXTERNAL (decl->csym) = 1;
|
|
}
|
|
else
|
|
{
|
|
/* Build the variable declaration. */
|
|
VarDeclaration *vd = decl->isVarDeclaration ();
|
|
gcc_assert (vd != NULL);
|
|
|
|
tree_code code = vd->isParameter () ? PARM_DECL
|
|
: !vd->canTakeAddressOf () ? CONST_DECL
|
|
: VAR_DECL;
|
|
decl->csym = build_decl (make_location_t (decl->loc), code,
|
|
get_identifier (decl->ident->toChars ()),
|
|
declaration_type (vd));
|
|
|
|
/* If any alignment was set on the declaration. */
|
|
if (!vd->alignment.isDefault ())
|
|
{
|
|
SET_DECL_ALIGN (decl->csym, vd->alignment.get () * BITS_PER_UNIT);
|
|
DECL_USER_ALIGN (decl->csym) = 1;
|
|
}
|
|
|
|
if (vd->storage_class & STCextern)
|
|
DECL_EXTERNAL (decl->csym) = 1;
|
|
|
|
/* CONST_DECL was initially intended for enumerals and may be used for
|
|
scalars in general, but not for aggregates. Here a non-constant
|
|
value is generated anyway so as the CONST_DECL only serves as a
|
|
placeholder for the value, however the DECL itself should never be
|
|
referenced in any generated code, or passed to the back-end. */
|
|
if (vd->storage_class & STCmanifest)
|
|
{
|
|
/* Cannot make an expression out of a void initializer. */
|
|
if (vd->_init && !vd->_init->isVoidInitializer ())
|
|
{
|
|
Expression *ie = initializerToExpression (vd->_init);
|
|
|
|
if (!vd->type->isscalar ())
|
|
DECL_INITIAL (decl->csym) = build_expr (ie, false);
|
|
else
|
|
DECL_INITIAL (decl->csym) = build_expr (ie, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the declaration mangled identifier if static. */
|
|
if (decl->isCodeseg () || decl->isDataseg ())
|
|
{
|
|
tree mangled_name;
|
|
|
|
if (decl->mangleOverride.length)
|
|
{
|
|
mangled_name =
|
|
get_identifier_with_length (decl->mangleOverride.ptr,
|
|
decl->mangleOverride.length);
|
|
}
|
|
else
|
|
mangled_name = get_identifier (d_mangle_decl (decl));
|
|
|
|
mangled_name = targetm.mangle_decl_assembler_name (decl->csym,
|
|
mangled_name);
|
|
/* The frontend doesn't handle duplicate definitions of unused symbols
|
|
with the same mangle. So a check is done here instead. */
|
|
if (IDENTIFIER_DSYMBOL (mangled_name))
|
|
{
|
|
Declaration *other = IDENTIFIER_DSYMBOL (mangled_name);
|
|
tree olddecl = decl->csym;
|
|
decl->csym = get_symbol_decl (other);
|
|
|
|
/* Update the symbol location to the current definition. */
|
|
if (DECL_EXTERNAL (decl->csym) && !DECL_INITIAL (decl->csym))
|
|
DECL_SOURCE_LOCATION (decl->csym) = DECL_SOURCE_LOCATION (olddecl);
|
|
|
|
/* The current declaration is a prototype or marked extern, merge
|
|
applied user attributes and return. */
|
|
if (DECL_EXTERNAL (olddecl) && !DECL_INITIAL (olddecl))
|
|
{
|
|
apply_user_attributes (decl, decl->csym);
|
|
return decl->csym;
|
|
}
|
|
/* The previous declaration is a prototype or marked extern, set the
|
|
current declaration as the main reference of the symbol. */
|
|
else if (DECL_EXTERNAL (decl->csym) && !DECL_INITIAL (decl->csym))
|
|
{
|
|
IDENTIFIER_DSYMBOL (mangled_name) = decl;
|
|
DECL_EXTERNAL (decl->csym) = 0;
|
|
}
|
|
/* Non-extern, non-templated decls shouldn't be defined twice. */
|
|
else if (!decl->isInstantiated ())
|
|
ScopeDsymbol::multiplyDefined (decl->loc, decl, other);
|
|
}
|
|
else
|
|
{
|
|
IDENTIFIER_PRETTY_NAME (mangled_name)
|
|
= get_identifier (decl->toPrettyChars (true));
|
|
IDENTIFIER_DSYMBOL (mangled_name) = decl;
|
|
|
|
SET_DECL_ASSEMBLER_NAME (decl->csym, mangled_name);
|
|
}
|
|
}
|
|
|
|
DECL_LANG_SPECIFIC (decl->csym) = build_lang_decl (decl);
|
|
DECL_CONTEXT (decl->csym) = d_decl_context (decl);
|
|
|
|
if (TREE_CODE (decl->csym) == PARM_DECL)
|
|
{
|
|
/* Pass non-trivial structs by invisible reference. */
|
|
if (TREE_ADDRESSABLE (TREE_TYPE (decl->csym)))
|
|
{
|
|
tree argtype = build_reference_type (TREE_TYPE (decl->csym));
|
|
argtype = build_qualified_type (argtype, TYPE_QUAL_RESTRICT);
|
|
gcc_assert (!DECL_BY_REFERENCE (decl->csym));
|
|
TREE_TYPE (decl->csym) = argtype;
|
|
DECL_BY_REFERENCE (decl->csym) = 1;
|
|
TREE_ADDRESSABLE (decl->csym) = 0;
|
|
relayout_decl (decl->csym);
|
|
decl->storage_class |= STCref;
|
|
}
|
|
|
|
DECL_ARG_TYPE (decl->csym) = TREE_TYPE (decl->csym);
|
|
gcc_assert (TREE_CODE (DECL_CONTEXT (decl->csym)) == FUNCTION_DECL);
|
|
}
|
|
else if (TREE_CODE (decl->csym) == CONST_DECL)
|
|
{
|
|
/* Manifest constants have no address in memory. */
|
|
TREE_CONSTANT (decl->csym) = 1;
|
|
TREE_READONLY (decl->csym) = 1;
|
|
}
|
|
else if (TREE_CODE (decl->csym) == FUNCTION_DECL)
|
|
{
|
|
/* Dual-context functions require the code generation to build an array
|
|
for the context pointer of the function, making the delicate task of
|
|
tracking which context to follow when encountering a non-local symbol,
|
|
and so are a not planned to be supported. */
|
|
if (fd->needThis () && !fd->isMember2 ())
|
|
{
|
|
fatal_error (make_location_t (fd->loc),
|
|
"function requires a dual-context, which is not yet "
|
|
"supported by GDC");
|
|
}
|
|
|
|
/* The real function type may differ from its declaration. */
|
|
tree fntype = TREE_TYPE (decl->csym);
|
|
tree newfntype = NULL_TREE;
|
|
|
|
if (fd->isNested ())
|
|
{
|
|
/* Add an extra argument for the frame/closure pointer, this is also
|
|
required to be compatible with D delegates. */
|
|
newfntype = build_vthis_function (void_type_node, fntype);
|
|
}
|
|
else if (fd->isThis ())
|
|
{
|
|
/* Add an extra argument for the `this' parameter. The handle type is
|
|
used even if there is no debug info. It is needed to make sure
|
|
virtual member functions are not called statically. */
|
|
AggregateDeclaration *ad = fd->isMember2 ();
|
|
tree handle = build_ctype (ad->handleType ());
|
|
|
|
/* If handle is a pointer type, get record type. */
|
|
if (!ad->isStructDeclaration ())
|
|
handle = TREE_TYPE (handle);
|
|
|
|
newfntype = build_vthis_function (handle, fntype);
|
|
|
|
/* Set the vindex on virtual functions. */
|
|
if (fd->isVirtual () && fd->vtblIndex != -1)
|
|
{
|
|
DECL_VINDEX (decl->csym) = size_int (fd->vtblIndex);
|
|
DECL_VIRTUAL_P (decl->csym) = 1;
|
|
}
|
|
|
|
/* Align method to the minimum boundary for target. */
|
|
SET_DECL_ALIGN (decl->csym, MINIMUM_METHOD_BOUNDARY);
|
|
}
|
|
else if (fd->isMain () || fd->isCMain ())
|
|
{
|
|
/* The main function is named `D main' to distinguish from C main. */
|
|
if (fd->isMain ())
|
|
DECL_NAME (decl->csym) = get_identifier (fd->toPrettyChars (true));
|
|
|
|
/* `void main' is implicitly converted to returning an int. */
|
|
newfntype = build_function_type (d_int_type, TYPE_ARG_TYPES (fntype));
|
|
}
|
|
|
|
if (newfntype != NULL_TREE)
|
|
{
|
|
/* Copy the old attributes from the original type. */
|
|
TYPE_ATTRIBUTES (newfntype) = TYPE_ATTRIBUTES (fntype);
|
|
TYPE_LANG_SPECIFIC (newfntype) = TYPE_LANG_SPECIFIC (fntype);
|
|
TREE_ADDRESSABLE (newfntype) = TREE_ADDRESSABLE (fntype);
|
|
TREE_TYPE (decl->csym) = newfntype;
|
|
d_keep (newfntype);
|
|
}
|
|
|
|
/* Miscellaneous function flags. */
|
|
|
|
/* In [pragma/inline], functions decorated with `pragma(inline)' affects
|
|
whether they are inlined or not. */
|
|
if (fd->inlining == PINLINE::always)
|
|
DECL_DECLARED_INLINE_P (decl->csym) = 1;
|
|
else if (fd->inlining == PINLINE::never)
|
|
DECL_UNINLINABLE (decl->csym) = 1;
|
|
|
|
/* In [pragma/crtctor], Annotates a function so it is run after the C
|
|
runtime library is initialized and before the D runtime library is
|
|
initialized. */
|
|
if (fd->isCrtCtor ())
|
|
{
|
|
DECL_STATIC_CONSTRUCTOR (decl->csym) = 1;
|
|
decl_init_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
|
|
}
|
|
else if (fd->isCrtDtor ())
|
|
{
|
|
DECL_STATIC_DESTRUCTOR (decl->csym) = 1;
|
|
decl_fini_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
|
|
}
|
|
|
|
/* Function was declared `naked'. */
|
|
if (fd->isNaked ())
|
|
{
|
|
insert_decl_attribute (decl->csym, "naked");
|
|
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl->csym) = 1;
|
|
}
|
|
|
|
/* Mark compiler generated functions as artificial. */
|
|
if (fd->isGenerated ())
|
|
DECL_ARTIFICIAL (decl->csym) = 1;
|
|
|
|
/* Ensure and require contracts are lexically nested in the function they
|
|
part of, but are always publicly callable. */
|
|
if (fd->ident == Identifier::idPool ("ensure")
|
|
|| fd->ident == Identifier::idPool ("require"))
|
|
TREE_PUBLIC (decl->csym) = 1;
|
|
|
|
if (decl->storage_class & STCfinal)
|
|
DECL_FINAL_P (decl->csym) = 1;
|
|
|
|
/* Function is of type `noreturn' or `typeof(*null)'. */
|
|
if (fd->type->nextOf ()->isTypeNoreturn ())
|
|
TREE_THIS_VOLATILE (decl->csym) = 1;
|
|
|
|
/* Check whether this function is expanded by the frontend. */
|
|
DECL_INTRINSIC_CODE (decl->csym) = INTRINSIC_NONE;
|
|
maybe_set_intrinsic (fd);
|
|
|
|
/* For nested functions in particular, unnest fndecl in the cgraph, as
|
|
all static chain passing is handled by the front-end. Do this even
|
|
if we are not emitting the body. */
|
|
struct cgraph_node *node = cgraph_node::get_create (decl->csym);
|
|
if (nested_function_origin (node))
|
|
unnest_function (node);
|
|
}
|
|
|
|
/* Mark compiler generated temporaries as artificial. */
|
|
if (decl->storage_class & STCtemp)
|
|
DECL_ARTIFICIAL (decl->csym) = 1;
|
|
|
|
/* Propagate shared on the decl. */
|
|
if (TYPE_SHARED (TREE_TYPE (decl->csym)))
|
|
TREE_ADDRESSABLE (decl->csym) = 1;
|
|
|
|
/* Symbol was marked volatile. */
|
|
if (decl->storage_class & STCvolatile)
|
|
TREE_THIS_VOLATILE (decl->csym) = 1;
|
|
|
|
/* Symbol was marked register. */
|
|
if (decl->storage_class & STCregister)
|
|
DECL_REGISTER (decl->csym) = 1;
|
|
|
|
/* Symbol was declared with deprecated attribute. */
|
|
if (decl->storage_class & STCdeprecated)
|
|
TREE_DEPRECATED (decl->csym) = 1;
|
|
|
|
if (decl->isDataseg () || decl->isCodeseg () || decl->isThreadlocal ())
|
|
{
|
|
/* Set TREE_PUBLIC by default, but allow private template to override. */
|
|
if (!fd || !fd->isNested ())
|
|
TREE_PUBLIC (decl->csym) = 1;
|
|
|
|
TREE_STATIC (decl->csym) = 1;
|
|
/* The decl has not been defined -- yet. */
|
|
DECL_EXTERNAL (decl->csym) = 1;
|
|
|
|
/* Visibility attributes are used by the debugger. */
|
|
set_visibility_for_decl (decl->csym, decl);
|
|
|
|
DECL_INSTANTIATED (decl->csym) = (decl->isInstantiated () != NULL);
|
|
set_linkage_for_decl (decl->csym);
|
|
}
|
|
|
|
/* Symbol is going in thread local storage. */
|
|
if (decl->isThreadlocal () && !DECL_ARTIFICIAL (decl->csym))
|
|
{
|
|
if (global.params.vtls)
|
|
message (decl->loc, "`%s` is thread local", decl->toChars ());
|
|
|
|
set_decl_tls_model (decl->csym, decl_default_tls_model (decl->csym));
|
|
}
|
|
|
|
/* Apply any user attributes that may affect semantic meaning. */
|
|
apply_user_attributes (decl, decl->csym);
|
|
|
|
/* Handle any conflicts between D language attributes and compiler-recognized
|
|
* user attributes. */
|
|
if (VAR_P (decl->csym) && DECL_HARD_REGISTER (decl->csym))
|
|
{
|
|
if (decl->storage_class & STCextern)
|
|
error_at (make_location_t (decl->loc), "explicit register variable "
|
|
"%qs declared %<extern%>", decl->toChars ());
|
|
else if (decl->isThreadlocal ())
|
|
error_at (make_location_t (decl->loc), "explicit register variable "
|
|
"%qs declared thread local", decl->toChars ());
|
|
}
|
|
|
|
/* %% Probably should be a little more intelligent about setting this. */
|
|
TREE_USED (decl->csym) = 1;
|
|
d_keep (decl->csym);
|
|
|
|
return maybe_build_decl_tree (decl);
|
|
}
|
|
|
|
/* Returns a declaration for a VAR_DECL. Used to create compiler-generated
|
|
global variables. */
|
|
|
|
tree
|
|
declare_extern_var (tree ident, tree type)
|
|
{
|
|
/* If the VAR_DECL has already been declared, return it. */
|
|
if (IDENTIFIER_DECL_TREE (ident))
|
|
return IDENTIFIER_DECL_TREE (ident);
|
|
|
|
tree name = IDENTIFIER_PRETTY_NAME (ident)
|
|
? IDENTIFIER_PRETTY_NAME (ident) : ident;
|
|
tree decl = build_decl (input_location, VAR_DECL, name, type);
|
|
|
|
IDENTIFIER_DECL_TREE (ident) = decl;
|
|
d_keep (decl);
|
|
|
|
SET_DECL_ASSEMBLER_NAME (decl, ident);
|
|
DECL_ARTIFICIAL (decl) = 1;
|
|
TREE_STATIC (decl) = 1;
|
|
TREE_PUBLIC (decl) = 1;
|
|
|
|
/* The decl has not been defined -- yet. */
|
|
DECL_EXTERNAL (decl) = 1;
|
|
|
|
set_linkage_for_decl (decl);
|
|
|
|
return decl;
|
|
}
|
|
|
|
/* Add local variable VAR into the current function body. */
|
|
|
|
void
|
|
declare_local_var (VarDeclaration *var)
|
|
{
|
|
gcc_assert (!var->isDataseg () && !var->isMember ());
|
|
gcc_assert (current_function_decl != NULL_TREE);
|
|
|
|
FuncDeclaration *fd = cfun->language->function;
|
|
tree decl = get_symbol_decl (var);
|
|
|
|
gcc_assert (!TREE_STATIC (decl));
|
|
d_pushdecl (decl);
|
|
DECL_CONTEXT (decl) = current_function_decl;
|
|
|
|
/* Compiler generated symbols. */
|
|
if (var == fd->vresult || var == fd->v_argptr)
|
|
DECL_ARTIFICIAL (decl) = 1;
|
|
|
|
if (DECL_LANG_FRAME_FIELD (decl))
|
|
{
|
|
/* Fixes debugging local variables. */
|
|
SET_DECL_VALUE_EXPR (decl, get_decl_tree (var));
|
|
DECL_HAS_VALUE_EXPR_P (decl) = 1;
|
|
}
|
|
}
|
|
|
|
/* Return an unnamed local temporary of type TYPE. */
|
|
|
|
tree
|
|
build_local_temp (tree type)
|
|
{
|
|
tree decl = create_tmp_var_raw (type);
|
|
d_pushdecl (decl);
|
|
|
|
return decl;
|
|
}
|
|
|
|
/* Return the correct decl to be used for DECL. For VAR_DECLs, this could
|
|
instead be a FIELD_DECL from a closure, or a RESULT_DECL from a named return
|
|
value. For PARM_DECLs, this could be a FIELD_DECL for a non-local `this'.
|
|
For all other kinds of decls, this just returns the result of
|
|
get_symbol_decl(). */
|
|
|
|
tree
|
|
get_decl_tree (Declaration *decl)
|
|
{
|
|
tree t = get_symbol_decl (decl);
|
|
FuncDeclaration *fd = cfun ? cfun->language->function : NULL;
|
|
VarDeclaration *vd = decl->isVarDeclaration ();
|
|
|
|
/* If cfun is NULL, then this is a global static. */
|
|
if (vd == NULL || fd == NULL)
|
|
return t;
|
|
|
|
/* Get the closure holding the var decl. */
|
|
if (DECL_LANG_FRAME_FIELD (t))
|
|
{
|
|
FuncDeclaration *parent = vd->toParent2 ()->isFuncDeclaration ();
|
|
tree frame_ref = get_framedecl (fd, parent);
|
|
|
|
tree field = component_ref (build_deref (frame_ref),
|
|
DECL_LANG_FRAME_FIELD (t));
|
|
/* Frame field can also be a reference to the DECL_RESULT of a function.
|
|
Dereference it to get the value. */
|
|
if (DECL_LANG_NRVO (t))
|
|
field = build_deref (field);
|
|
|
|
return field;
|
|
}
|
|
|
|
/* Get the named return value. */
|
|
if (DECL_LANG_NRVO (t))
|
|
return DECL_LANG_NRVO (t);
|
|
|
|
/* Get the non-local `this' value by going through parent link
|
|
of nested classes, this routine pretty much undoes what
|
|
getRightThis in the frontend removes from codegen. */
|
|
if (vd->parent != fd && vd->isThisDeclaration ())
|
|
{
|
|
/* Find the first parent that is a member function. */
|
|
while (!fd->isMember2 ())
|
|
{
|
|
gcc_assert (fd->vthis);
|
|
fd = fd->toParent2 ()->isFuncDeclaration ();
|
|
gcc_assert (fd != NULL);
|
|
}
|
|
|
|
AggregateDeclaration *ad = fd->isThis ();
|
|
gcc_assert (ad != NULL);
|
|
|
|
/* The parent function is for the same `this' declaration we are
|
|
building a chain to. Non-local declaration is inaccessible. */
|
|
if (fd->vthis == vd)
|
|
return error_no_frame_access (fd);
|
|
|
|
t = get_decl_tree (fd->vthis);
|
|
Dsymbol *outer = fd;
|
|
|
|
while (outer != vd->parent)
|
|
{
|
|
gcc_assert (ad != NULL);
|
|
outer = ad->toParent2 ();
|
|
|
|
/* Get the this->this parent link. */
|
|
tree vfield = get_symbol_decl (ad->vthis);
|
|
t = component_ref (build_deref (t), vfield);
|
|
|
|
ad = outer->isAggregateDeclaration ();
|
|
if (ad != NULL)
|
|
continue;
|
|
|
|
fd = outer->isFuncDeclaration ();
|
|
while (fd != NULL)
|
|
{
|
|
/* If outer function creates a closure, then the `this'
|
|
value would be the closure pointer, and the real
|
|
`this' the first field of that closure. */
|
|
tree ff = get_frameinfo (fd);
|
|
if (FRAMEINFO_CREATES_FRAME (ff))
|
|
{
|
|
t = build_nop (build_pointer_type (FRAMEINFO_TYPE (ff)), t);
|
|
t = indirect_ref (build_ctype (fd->vthis->type), t);
|
|
}
|
|
|
|
if (fd == vd->parent)
|
|
break;
|
|
|
|
/* Continue looking for the right `this'. */
|
|
outer = outer->toParent2 ();
|
|
fd = outer->isFuncDeclaration ();
|
|
}
|
|
|
|
ad = outer->isAggregateDeclaration ();
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Auto variable that the back end will handle for us. */
|
|
return t;
|
|
}
|
|
|
|
/* Finish up a variable declaration and compile it all the way to
|
|
the assembler language output. */
|
|
|
|
void
|
|
d_finish_decl (tree decl)
|
|
{
|
|
gcc_assert (!error_operand_p (decl));
|
|
|
|
/* We are sending this symbol to object file, can't be extern. */
|
|
TREE_STATIC (decl) = 1;
|
|
DECL_EXTERNAL (decl) = 0;
|
|
|
|
/* Update the TLS model as the linkage has been modified. */
|
|
if (DECL_THREAD_LOCAL_P (decl))
|
|
set_decl_tls_model (decl, decl_default_tls_model (decl));
|
|
|
|
relayout_decl (decl);
|
|
|
|
if (flag_checking && DECL_INITIAL (decl))
|
|
{
|
|
/* Initializer must never be bigger than symbol size. */
|
|
HOST_WIDE_INT tsize = int_size_in_bytes (TREE_TYPE (decl));
|
|
HOST_WIDE_INT dtsize =
|
|
int_size_in_bytes (TREE_TYPE (DECL_INITIAL (decl)));
|
|
|
|
if (tsize < dtsize)
|
|
{
|
|
tree name = DECL_ASSEMBLER_NAME (decl);
|
|
|
|
internal_error ("mismatch between declaration %qE size (%wd) and "
|
|
"its initializer size (%wd)",
|
|
IDENTIFIER_PRETTY_NAME (name)
|
|
? IDENTIFIER_PRETTY_NAME (name) : name,
|
|
tsize, dtsize);
|
|
}
|
|
}
|
|
|
|
/* Without weak symbols, symbol should be put in .common, but that can't
|
|
be done if there is a nonzero initializer. */
|
|
if (DECL_COMDAT (decl) && DECL_COMMON (decl)
|
|
&& initializer_zerop (DECL_INITIAL (decl)))
|
|
DECL_INITIAL (decl) = error_mark_node;
|
|
|
|
/* Add this decl to the current binding level. */
|
|
d_pushdecl (decl);
|
|
|
|
rest_of_decl_compilation (decl, 1, 0);
|
|
}
|
|
|
|
/* Thunk code is based on g++. */
|
|
|
|
static int thunk_labelno;
|
|
|
|
/* Create a static alias to function. */
|
|
|
|
static tree
|
|
make_alias_for_thunk (tree function)
|
|
{
|
|
tree alias;
|
|
char buf[256];
|
|
|
|
/* Thunks may reference extern functions which cannot be aliased. */
|
|
if (DECL_EXTERNAL (function))
|
|
return function;
|
|
|
|
targetm.asm_out.generate_internal_label (buf, "LTHUNK", thunk_labelno);
|
|
thunk_labelno++;
|
|
|
|
alias = build_decl (DECL_SOURCE_LOCATION (function), FUNCTION_DECL,
|
|
get_identifier (buf), TREE_TYPE (function));
|
|
DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
|
|
lang_hooks.dup_lang_specific_decl (alias);
|
|
DECL_CONTEXT (alias) = NULL_TREE;
|
|
TREE_READONLY (alias) = TREE_READONLY (function);
|
|
TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
|
|
TREE_PUBLIC (alias) = 0;
|
|
|
|
DECL_EXTERNAL (alias) = 0;
|
|
DECL_ARTIFICIAL (alias) = 1;
|
|
|
|
DECL_DECLARED_INLINE_P (alias) = 0;
|
|
DECL_INITIAL (alias) = error_mark_node;
|
|
DECL_ARGUMENTS (alias) = copy_list (DECL_ARGUMENTS (function));
|
|
|
|
TREE_ADDRESSABLE (alias) = 1;
|
|
TREE_USED (alias) = 1;
|
|
SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
|
|
|
|
if (!flag_syntax_only)
|
|
{
|
|
cgraph_node *aliasn;
|
|
aliasn = cgraph_node::create_same_body_alias (alias, function);
|
|
gcc_assert (aliasn != NULL);
|
|
}
|
|
return alias;
|
|
}
|
|
|
|
/* Emit the definition of a D vtable thunk. */
|
|
|
|
static void
|
|
finish_thunk (tree thunk, tree function)
|
|
{
|
|
/* Setup how D thunks are outputted. */
|
|
int fixed_offset = -THUNK_LANG_OFFSET (thunk);
|
|
bool this_adjusting = true;
|
|
tree alias;
|
|
|
|
if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function))
|
|
alias = make_alias_for_thunk (function);
|
|
else
|
|
alias = function;
|
|
|
|
TREE_ADDRESSABLE (function) = 1;
|
|
TREE_USED (function) = 1;
|
|
|
|
if (flag_syntax_only)
|
|
{
|
|
TREE_ASM_WRITTEN (thunk) = 1;
|
|
return;
|
|
}
|
|
|
|
if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
|
|
&& targetm_common.have_named_sections)
|
|
{
|
|
tree fn = function;
|
|
symtab_node *symbol = symtab_node::get (function);
|
|
|
|
if (symbol != NULL && symbol->alias)
|
|
{
|
|
if (symbol->analyzed)
|
|
fn = symtab_node::get (function)->ultimate_alias_target ()->decl;
|
|
else
|
|
fn = symtab_node::get (function)->alias_target;
|
|
}
|
|
resolve_unique_section (fn, 0, flag_function_sections);
|
|
|
|
if (DECL_SECTION_NAME (fn) != NULL && DECL_ONE_ONLY (fn))
|
|
{
|
|
resolve_unique_section (thunk, 0, flag_function_sections);
|
|
|
|
/* Output the thunk into the same section as function. */
|
|
set_decl_section_name (thunk, fn);
|
|
symtab_node::get (thunk)->implicit_section
|
|
= symtab_node::get (fn)->implicit_section;
|
|
}
|
|
}
|
|
|
|
/* Set up cloned argument trees for the thunk. */
|
|
tree t = NULL_TREE;
|
|
for (tree a = DECL_ARGUMENTS (function); a; a = DECL_CHAIN (a))
|
|
{
|
|
tree x = copy_node (a);
|
|
DECL_CHAIN (x) = t;
|
|
DECL_CONTEXT (x) = thunk;
|
|
SET_DECL_RTL (x, NULL);
|
|
DECL_HAS_VALUE_EXPR_P (x) = 0;
|
|
TREE_ADDRESSABLE (x) = 0;
|
|
t = x;
|
|
}
|
|
DECL_ARGUMENTS (thunk) = nreverse (t);
|
|
TREE_ASM_WRITTEN (thunk) = 1;
|
|
|
|
cgraph_node *funcn, *thunk_node;
|
|
|
|
funcn = cgraph_node::get_create (function);
|
|
gcc_assert (funcn);
|
|
thunk_node = funcn->create_thunk (thunk, thunk, this_adjusting,
|
|
fixed_offset, 0, 0, 0, alias);
|
|
|
|
if (DECL_ONE_ONLY (function))
|
|
thunk_node->add_to_same_comdat_group (funcn);
|
|
}
|
|
|
|
/* Return a thunk to DECL. Thunks adjust the incoming `this' pointer by OFFSET.
|
|
Adjustor thunks are created and pointers to them stored in the method entries
|
|
in the vtable in order to set the this pointer to the start of the object
|
|
instance corresponding to the implementing method. */
|
|
|
|
tree
|
|
make_thunk (FuncDeclaration *decl, int offset)
|
|
{
|
|
tree function = get_symbol_decl (decl);
|
|
|
|
if (!DECL_ARGUMENTS (function) || !DECL_RESULT (function))
|
|
{
|
|
/* Compile the function body before generating the thunk, this is done
|
|
even if the decl is external to the current module. */
|
|
if (decl->fbody)
|
|
build_decl_tree (decl);
|
|
else
|
|
{
|
|
/* Build parameters for functions that are not being compiled,
|
|
so that they can be correctly cloned in finish_thunk. */
|
|
tree fntype = TREE_TYPE (function);
|
|
tree params = NULL_TREE;
|
|
|
|
for (tree t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
|
|
{
|
|
if (t == void_list_node)
|
|
break;
|
|
|
|
tree param = build_decl (DECL_SOURCE_LOCATION (function),
|
|
PARM_DECL, NULL_TREE, TREE_VALUE (t));
|
|
DECL_ARG_TYPE (param) = TREE_TYPE (param);
|
|
DECL_ARTIFICIAL (param) = 1;
|
|
DECL_IGNORED_P (param) = 1;
|
|
DECL_CONTEXT (param) = function;
|
|
params = chainon (params, param);
|
|
}
|
|
|
|
DECL_ARGUMENTS (function) = params;
|
|
|
|
/* Also build the result decl, which is needed when force creating
|
|
the thunk in gimple inside cgraph_node::expand_thunk. */
|
|
tree resdecl = build_decl (DECL_SOURCE_LOCATION (function),
|
|
RESULT_DECL, NULL_TREE,
|
|
TREE_TYPE (fntype));
|
|
DECL_ARTIFICIAL (resdecl) = 1;
|
|
DECL_IGNORED_P (resdecl) = 1;
|
|
DECL_CONTEXT (resdecl) = function;
|
|
DECL_RESULT (function) = resdecl;
|
|
}
|
|
}
|
|
|
|
/* Don't build the thunk if the compilation step failed. */
|
|
if (global.errors)
|
|
return error_mark_node;
|
|
|
|
/* See if we already have the thunk in question. */
|
|
for (tree t = DECL_LANG_THUNKS (function); t; t = DECL_CHAIN (t))
|
|
{
|
|
if (THUNK_LANG_OFFSET (t) == offset)
|
|
return t;
|
|
}
|
|
|
|
tree thunk = build_decl (DECL_SOURCE_LOCATION (function),
|
|
FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
|
|
DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
|
|
lang_hooks.dup_lang_specific_decl (thunk);
|
|
THUNK_LANG_OFFSET (thunk) = offset;
|
|
|
|
TREE_READONLY (thunk) = TREE_READONLY (function);
|
|
TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
|
|
TREE_NOTHROW (thunk) = TREE_NOTHROW (function);
|
|
|
|
DECL_CONTEXT (thunk) = d_decl_context (decl);
|
|
|
|
/* Thunks inherit the public access of the function they are targeting.
|
|
Thunks are connected to the definitions of the functions, so thunks are
|
|
not produced for external functions. */
|
|
TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
|
|
DECL_EXTERNAL (thunk) = DECL_EXTERNAL (function);
|
|
|
|
/* Thunks are always addressable. */
|
|
TREE_ADDRESSABLE (thunk) = 1;
|
|
TREE_USED (thunk) = 1;
|
|
DECL_ARTIFICIAL (thunk) = 1;
|
|
DECL_DECLARED_INLINE_P (thunk) = 0;
|
|
|
|
if (TREE_PUBLIC (thunk))
|
|
{
|
|
DECL_VISIBILITY (thunk) = DECL_VISIBILITY (function);
|
|
DECL_COMDAT (thunk) = DECL_COMDAT (function);
|
|
DECL_WEAK (thunk) = DECL_WEAK (function);
|
|
}
|
|
|
|
/* When the thunk is for an extern C++ function, let C++ do the thunk
|
|
generation and just reference the symbol as extern, instead of
|
|
forcing a D local thunk to be emitted. */
|
|
const char *ident;
|
|
|
|
if (decl->resolvedLinkage () == LINK::cpp)
|
|
ident = target.cpp.thunkMangle (decl, offset);
|
|
else
|
|
{
|
|
tree target_name = DECL_ASSEMBLER_NAME (function);
|
|
unsigned identlen = IDENTIFIER_LENGTH (target_name) + 14;
|
|
ident = XNEWVEC (const char, identlen);
|
|
|
|
snprintf (CONST_CAST (char *, ident), identlen,
|
|
"_DTi%u%s", offset, IDENTIFIER_POINTER (target_name));
|
|
}
|
|
|
|
DECL_NAME (thunk) = get_identifier (ident);
|
|
SET_DECL_ASSEMBLER_NAME (thunk, DECL_NAME (thunk));
|
|
|
|
d_keep (thunk);
|
|
|
|
if (decl->resolvedLinkage () != LINK::cpp)
|
|
free (CONST_CAST (char *, ident));
|
|
|
|
if (!DECL_EXTERNAL (function))
|
|
finish_thunk (thunk, function);
|
|
|
|
/* Add it to the list of thunks associated with the function. */
|
|
DECL_LANG_THUNKS (thunk) = NULL_TREE;
|
|
DECL_CHAIN (thunk) = DECL_LANG_THUNKS (function);
|
|
DECL_LANG_THUNKS (function) = thunk;
|
|
|
|
return thunk;
|
|
}
|
|
|
|
/* Create the FUNCTION_DECL for a function definition.
|
|
This function creates a binding context for the function body
|
|
as well as setting up the FUNCTION_DECL in current_function_decl.
|
|
Returns the previous function context if it was already set. */
|
|
|
|
tree
|
|
start_function (FuncDeclaration *fd)
|
|
{
|
|
tree fndecl = get_symbol_decl (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. */
|
|
if (function_defined_in_root_p (fd))
|
|
DECL_EXTERNAL (fndecl) = 0;
|
|
|
|
DECL_INITIAL (fndecl) = error_mark_node;
|
|
|
|
/* Add this decl to the current binding level. */
|
|
d_pushdecl (fndecl);
|
|
|
|
/* Save the current function context. */
|
|
tree old_context = current_function_decl;
|
|
|
|
if (old_context)
|
|
push_function_context ();
|
|
|
|
/* Let GCC know the current scope is this function. */
|
|
current_function_decl = fndecl;
|
|
|
|
tree restype = TREE_TYPE (TREE_TYPE (fndecl));
|
|
tree resdecl = build_decl (make_location_t (fd->loc), RESULT_DECL,
|
|
NULL_TREE, restype);
|
|
|
|
DECL_RESULT (fndecl) = resdecl;
|
|
DECL_CONTEXT (resdecl) = fndecl;
|
|
DECL_ARTIFICIAL (resdecl) = 1;
|
|
DECL_IGNORED_P (resdecl) = 1;
|
|
|
|
/* Initialize the RTL code for the function. */
|
|
allocate_struct_function (fndecl, false);
|
|
|
|
/* Store the end of the function. */
|
|
if (fd->endloc.filename)
|
|
cfun->function_end_locus = make_location_t (fd->endloc);
|
|
else
|
|
cfun->function_end_locus = DECL_SOURCE_LOCATION (fndecl);
|
|
|
|
cfun->language = ggc_cleared_alloc <language_function> ();
|
|
cfun->language->function = fd;
|
|
|
|
/* Default chain value is `null' unless parent found. */
|
|
cfun->language->static_chain = null_pointer_node;
|
|
|
|
/* Find module for this function. */
|
|
for (Dsymbol *p = fd->parent; p != NULL; p = p->parent)
|
|
{
|
|
cfun->language->module = p->isModule ();
|
|
if (cfun->language->module)
|
|
break;
|
|
}
|
|
gcc_assert (cfun->language->module != NULL);
|
|
|
|
/* Begin the statement tree for this function. */
|
|
push_stmt_list ();
|
|
push_binding_level (level_function);
|
|
|
|
return old_context;
|
|
}
|
|
|
|
/* Finish up a function declaration and compile that function all
|
|
the way to assembler language output. The free the storage for
|
|
the function definition. Restores the previous function context. */
|
|
|
|
void
|
|
finish_function (tree old_context)
|
|
{
|
|
tree fndecl = current_function_decl;
|
|
|
|
/* Tie off the statement tree for this function. */
|
|
tree block = pop_binding_level ();
|
|
tree body = pop_stmt_list ();
|
|
tree bind = build3 (BIND_EXPR, void_type_node,
|
|
BLOCK_VARS (block), body, block);
|
|
|
|
gcc_assert (vec_safe_is_empty (d_function_chain->stmt_list));
|
|
|
|
/* Back-end expects a statement list to come from somewhere, however
|
|
pop_stmt_list returns expressions when there is a single statement.
|
|
So here we create a statement list unconditionally. */
|
|
if (TREE_CODE (body) != STATEMENT_LIST)
|
|
{
|
|
tree stmtlist = alloc_stmt_list ();
|
|
append_to_statement_list_force (body, &stmtlist);
|
|
BIND_EXPR_BODY (bind) = stmtlist;
|
|
}
|
|
else if (!STATEMENT_LIST_HEAD (body))
|
|
{
|
|
/* For empty functions add a void return. */
|
|
append_to_statement_list_force (return_expr (NULL_TREE), &body);
|
|
}
|
|
|
|
DECL_SAVED_TREE (fndecl) = bind;
|
|
|
|
if (!errorcount && !global.errors)
|
|
{
|
|
/* Dump the D-specific tree IR. */
|
|
dump_function (TDI_original, fndecl);
|
|
|
|
cgraph_node::finalize_function (fndecl, true);
|
|
}
|
|
|
|
/* We're leaving the context of this function, so free it. */
|
|
ggc_free (cfun->language);
|
|
cfun->language = NULL;
|
|
set_cfun (NULL);
|
|
|
|
if (old_context)
|
|
pop_function_context ();
|
|
|
|
current_function_decl = old_context;
|
|
}
|
|
|
|
/* Mark DECL, which is a VAR_DECL or FUNCTION_DECL as a symbol that
|
|
must be emitted in this, output module. */
|
|
|
|
static void
|
|
d_mark_needed (tree decl)
|
|
{
|
|
TREE_USED (decl) = 1;
|
|
|
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
|
{
|
|
struct cgraph_node *node = cgraph_node::get_create (decl);
|
|
node->forced_by_abi = true;
|
|
}
|
|
else if (VAR_P (decl))
|
|
{
|
|
struct varpool_node *node = varpool_node::get_create (decl);
|
|
node->forced_by_abi = true;
|
|
}
|
|
}
|
|
|
|
/* Get the VAR_DECL of the vtable symbol for DECL. If this does not yet exist,
|
|
create it. The vtable is accessible via ClassInfo, but since it is needed
|
|
frequently (like for rtti comparisons), make it directly accessible. */
|
|
|
|
tree
|
|
get_vtable_decl (ClassDeclaration *decl)
|
|
{
|
|
if (decl->vtblsym && decl->vtblsym->csym)
|
|
return decl->vtblsym->csym;
|
|
|
|
tree ident = mangle_internal_decl (decl, "__vtbl", "Z");
|
|
/* Note: Using a static array type for the VAR_DECL, the DECL_INITIAL value
|
|
will have a different type. However the back-end seems to accept this. */
|
|
tree type = build_ctype (Type::tvoidptr->sarrayOf (decl->vtbl.length));
|
|
|
|
Dsymbol *vtblsym = decl->vtblSymbol ();
|
|
vtblsym->csym = declare_extern_var (ident, type);
|
|
DECL_LANG_SPECIFIC (vtblsym->csym) = build_lang_decl (NULL);
|
|
|
|
/* Class is a reference, want the record type. */
|
|
DECL_CONTEXT (vtblsym->csym) = TREE_TYPE (build_ctype (decl->type));
|
|
TREE_READONLY (vtblsym->csym) = 1;
|
|
DECL_VIRTUAL_P (vtblsym->csym) = 1;
|
|
|
|
SET_DECL_ALIGN (vtblsym->csym, TARGET_VTABLE_ENTRY_ALIGN);
|
|
DECL_USER_ALIGN (vtblsym->csym) = true;
|
|
|
|
return vtblsym->csym;
|
|
}
|
|
|
|
/* Helper function of build_class_instance. Find the field inside aggregate
|
|
TYPE identified by IDENT at field OFFSET. */
|
|
|
|
static tree
|
|
find_aggregate_field (tree type, tree ident, tree offset)
|
|
{
|
|
tree fields = TYPE_FIELDS (type);
|
|
|
|
for (tree field = fields; field != NULL_TREE; field = TREE_CHAIN (field))
|
|
{
|
|
if (DECL_NAME (field) == NULL_TREE
|
|
&& RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
|
|
&& ANON_AGGR_TYPE_P (TREE_TYPE (field)))
|
|
{
|
|
/* Search nesting anonymous structs and unions. */
|
|
tree vfield = find_aggregate_field (TREE_TYPE (field),
|
|
ident, offset);
|
|
if (vfield != NULL_TREE)
|
|
return vfield;
|
|
}
|
|
else if (DECL_NAME (field) == ident
|
|
&& (offset == NULL_TREE
|
|
|| DECL_FIELD_OFFSET (field) == offset))
|
|
{
|
|
/* Found matching field at offset. */
|
|
return field;
|
|
}
|
|
}
|
|
|
|
return NULL_TREE;
|
|
}
|
|
|
|
/* Helper function of build_new_class_expr. Return a constructor that matches
|
|
the layout of the class expression EXP. */
|
|
|
|
static tree
|
|
build_class_instance (ClassReferenceExp *exp)
|
|
{
|
|
ClassDeclaration *cd = exp->originalClass ();
|
|
tree type = TREE_TYPE (build_ctype (exp->value->stype));
|
|
vec <constructor_elt, va_gc> *ve = NULL;
|
|
|
|
/* The set base vtable field. */
|
|
tree vptr = build_address (get_vtable_decl (cd));
|
|
CONSTRUCTOR_APPEND_ELT (ve, TYPE_FIELDS (type), vptr);
|
|
|
|
/* Go through the inheritance graph from top to bottom. This will add all
|
|
values to the constructor out of order, however build_struct_literal
|
|
will re-order all values before returning the finished literal. */
|
|
for (ClassDeclaration *bcd = cd; bcd != NULL; bcd = bcd->baseClass)
|
|
{
|
|
/* Anonymous vtable interface fields are laid out before the fields of
|
|
each class. The interface offset is used to determine where to put
|
|
the classinfo offset reference. */
|
|
for (size_t i = 0; i < bcd->vtblInterfaces->length; i++)
|
|
{
|
|
BaseClass *bc = (*bcd->vtblInterfaces)[i];
|
|
|
|
for (ClassDeclaration *cd2 = cd; 1; cd2 = cd2->baseClass)
|
|
{
|
|
gcc_assert (cd2 != NULL);
|
|
|
|
unsigned csymoffset = base_vtable_offset (cd2, bc);
|
|
/* If the base class vtable was found. */
|
|
if (csymoffset != ~0u)
|
|
{
|
|
tree csym = build_address (get_classinfo_decl (cd2));
|
|
csym = build_offset (csym, size_int (csymoffset));
|
|
|
|
tree field = find_aggregate_field (type, NULL_TREE,
|
|
size_int (bc->offset));
|
|
gcc_assert (field != NULL_TREE);
|
|
|
|
CONSTRUCTOR_APPEND_ELT (ve, field, csym);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generate initial values of all fields owned by current class.
|
|
Use both the name and offset to find the right field. */
|
|
for (size_t i = 0; i < bcd->fields.length; i++)
|
|
{
|
|
VarDeclaration *vfield = bcd->fields[i];
|
|
int index = exp->findFieldIndexByName (vfield);
|
|
gcc_assert (index != -1);
|
|
|
|
Expression *value = (*exp->value->elements)[index];
|
|
if (!value)
|
|
continue;
|
|
|
|
/* Use find_aggregate_field to get the overridden field decl,
|
|
instead of the field associated with the base class. */
|
|
tree field = get_symbol_decl (bcd->fields[i]);
|
|
field = find_aggregate_field (type, DECL_NAME (field),
|
|
DECL_FIELD_OFFSET (field));
|
|
gcc_assert (field != NULL_TREE);
|
|
|
|
CONSTRUCTOR_APPEND_ELT (ve, field, build_expr (value, true));
|
|
}
|
|
}
|
|
|
|
return build_struct_literal (type, ve);
|
|
}
|
|
|
|
/* Get the VAR_DECL of a class instance representing EXPR as static data.
|
|
If this does not yet exist, create it. This is used to support initializing
|
|
a static variable that is of a class type using values known during CTFE.
|
|
In user code, it is analogous to the following code snippet.
|
|
|
|
enum E = new C(1, 2, 3);
|
|
|
|
That we write the contents of `C(1, 2, 3)' to static data is only a compiler
|
|
implementation detail. The initialization of these symbols could be done at
|
|
run-time using during as part of the module initialization or shared static
|
|
constructors phase of run-time start-up - whichever comes after `gc_init()'.
|
|
And infact that would be the better thing to do here eventually. */
|
|
|
|
tree
|
|
build_new_class_expr (ClassReferenceExp *expr)
|
|
{
|
|
if (expr->value->sym)
|
|
return expr->value->sym;
|
|
|
|
/* Build the reference symbol. */
|
|
tree type = build_ctype (expr->value->stype);
|
|
expr->value->sym = build_artificial_decl (TREE_TYPE (type), NULL_TREE, "C");
|
|
|
|
DECL_INITIAL (expr->value->sym) = build_class_instance (expr);
|
|
d_pushdecl (expr->value->sym);
|
|
rest_of_decl_compilation (expr->value->sym, 1, 0);
|
|
|
|
return expr->value->sym;
|
|
}
|
|
|
|
/* Get the VAR_DECL of the static initializer symbol for the struct/class DECL.
|
|
If this does not yet exist, create it. The static initializer data is
|
|
accessible via TypeInfo, and is also used in `new class' and default
|
|
initializing struct literals. */
|
|
|
|
tree
|
|
aggregate_initializer_decl (AggregateDeclaration *decl)
|
|
{
|
|
if (decl->sinit)
|
|
return (tree) decl->sinit;
|
|
|
|
/* Class is a reference, want the record type. */
|
|
tree type = build_ctype (decl->type);
|
|
StructDeclaration *sd = decl->isStructDeclaration ();
|
|
if (!sd)
|
|
type = TREE_TYPE (type);
|
|
|
|
tree ident = mangle_internal_decl (decl, "__init", "Z");
|
|
|
|
tree sinit = declare_extern_var (ident, type);
|
|
DECL_LANG_SPECIFIC (sinit) = build_lang_decl (NULL);
|
|
|
|
DECL_CONTEXT (sinit) = type;
|
|
TREE_READONLY (sinit) = 1;
|
|
|
|
/* Honor struct alignment set by user. */
|
|
if (sd && !sd->alignment.isDefault ())
|
|
{
|
|
SET_DECL_ALIGN (sinit, sd->alignment.get () * BITS_PER_UNIT);
|
|
DECL_USER_ALIGN (sinit) = true;
|
|
}
|
|
|
|
decl->sinit = sinit;
|
|
return sinit;
|
|
}
|
|
|
|
/* Generate the data for the static initializer. */
|
|
|
|
tree
|
|
layout_class_initializer (ClassDeclaration *cd)
|
|
{
|
|
NewExp *ne = NewExp::create (cd->loc, NULL, cd->type, NULL);
|
|
ne->type = cd->type;
|
|
|
|
Expression *e = ne->ctfeInterpret ();
|
|
gcc_assert (e->op == EXP::classReference);
|
|
|
|
return build_class_instance (e->isClassReferenceExp ());
|
|
}
|
|
|
|
tree
|
|
layout_struct_initializer (StructDeclaration *sd)
|
|
{
|
|
StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL);
|
|
|
|
if (!sd->fill (sd->loc, sle->elements, true))
|
|
gcc_unreachable ();
|
|
|
|
sle->type = sd->type;
|
|
return build_expr (sle, true);
|
|
}
|
|
|
|
/* Get the VAR_DECL of the static initializer symbol for the enum DECL.
|
|
If this does not yet exist, create it. The static initializer data is
|
|
accessible via TypeInfo_Enum, but the field member type is a byte[] that
|
|
requires a pointer to a symbol reference. */
|
|
|
|
tree
|
|
enum_initializer_decl (EnumDeclaration *decl)
|
|
{
|
|
if (decl->sinit)
|
|
return decl->sinit;
|
|
|
|
gcc_assert (decl->ident);
|
|
|
|
tree type = build_ctype (decl->type);
|
|
tree ident = mangle_internal_decl (decl, "__init", "Z");
|
|
|
|
decl->sinit = declare_extern_var (ident, type);
|
|
DECL_LANG_SPECIFIC (decl->sinit) = build_lang_decl (NULL);
|
|
|
|
DECL_CONTEXT (decl->sinit) = d_decl_context (decl);
|
|
TREE_READONLY (decl->sinit) = 1;
|
|
|
|
return decl->sinit;
|
|
}
|
|
|
|
/* Return an anonymous static variable of type TYPE, initialized with INIT,
|
|
and optionally prefixing the name with PREFIX. */
|
|
|
|
tree
|
|
build_artificial_decl (tree type, tree init, const char *prefix)
|
|
{
|
|
tree decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, NULL_TREE, type);
|
|
const char *name = prefix ? prefix : "___s";
|
|
char *label;
|
|
|
|
ASM_FORMAT_PRIVATE_NAME (label, name, DECL_UID (decl));
|
|
SET_DECL_ASSEMBLER_NAME (decl, get_identifier (label));
|
|
DECL_NAME (decl) = DECL_ASSEMBLER_NAME (decl);
|
|
|
|
TREE_PUBLIC (decl) = 0;
|
|
TREE_STATIC (decl) = 1;
|
|
TREE_USED (decl) = 1;
|
|
DECL_IGNORED_P (decl) = 1;
|
|
DECL_ARTIFICIAL (decl) = 1;
|
|
|
|
/* Perhaps at some point the initializer constant should be hashed
|
|
to remove duplicates. */
|
|
DECL_INITIAL (decl) = init;
|
|
|
|
return decl;
|
|
}
|
|
|
|
/* Build TYPE_DECL for the declaration DSYM. */
|
|
|
|
void
|
|
build_type_decl (tree type, Dsymbol *dsym)
|
|
{
|
|
if (TYPE_STUB_DECL (type))
|
|
return;
|
|
|
|
/* If a templated type, use the template instance name, as that includes all
|
|
template parameters. */
|
|
const char *name = dsym->parent->isTemplateInstance ()
|
|
? ((TemplateInstance *) dsym->parent)->toChars () : dsym->ident->toChars ();
|
|
|
|
tree decl = build_decl (make_location_t (dsym->loc), TYPE_DECL,
|
|
get_identifier (name), type);
|
|
SET_DECL_ASSEMBLER_NAME (decl, get_identifier (d_mangle_decl (dsym)));
|
|
TREE_PUBLIC (decl) = 1;
|
|
DECL_CONTEXT (decl) = d_decl_context (dsym);
|
|
|
|
TYPE_CONTEXT (type) = DECL_CONTEXT (decl);
|
|
TYPE_NAME (type) = decl;
|
|
|
|
/* Not sure if there is a need for separate TYPE_DECLs in
|
|
TYPE_NAME and TYPE_STUB_DECL. */
|
|
if (TREE_CODE (type) == ENUMERAL_TYPE || RECORD_OR_UNION_TYPE_P (type))
|
|
{
|
|
DECL_ARTIFICIAL (decl) = 1;
|
|
TYPE_STUB_DECL (type) = decl;
|
|
}
|
|
else if (type != TYPE_MAIN_VARIANT (type))
|
|
DECL_ORIGINAL_TYPE (decl) = TYPE_MAIN_VARIANT (type);
|
|
}
|
|
|
|
/* Create a declaration for field NAME of a given TYPE, setting the flags
|
|
for whether the field is ARTIFICIAL and/or IGNORED. */
|
|
|
|
tree
|
|
create_field_decl (tree type, const char *name, int artificial, int ignored)
|
|
{
|
|
tree decl = build_decl (input_location, FIELD_DECL,
|
|
name ? get_identifier (name) : NULL_TREE, type);
|
|
DECL_ARTIFICIAL (decl) = artificial;
|
|
DECL_IGNORED_P (decl) = ignored;
|
|
|
|
return decl;
|
|
}
|
|
|
|
/* Return the COMDAT group into which DECL should be placed. */
|
|
|
|
static tree
|
|
d_comdat_group (tree decl)
|
|
{
|
|
/* If already part of a comdat group, use that. */
|
|
if (DECL_COMDAT_GROUP (decl))
|
|
return DECL_COMDAT_GROUP (decl);
|
|
|
|
return DECL_ASSEMBLER_NAME (decl);
|
|
}
|
|
|
|
/* Set DECL up to have the closest approximation of "initialized common"
|
|
linkage available. */
|
|
|
|
static void
|
|
d_comdat_linkage (tree decl)
|
|
{
|
|
/* COMDAT definitions have to be public. */
|
|
gcc_assert (TREE_PUBLIC (decl));
|
|
|
|
if (supports_one_only ())
|
|
make_decl_one_only (decl, d_comdat_group (decl));
|
|
else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INSTANTIATED (decl))
|
|
|| (VAR_P (decl) && DECL_ARTIFICIAL (decl)))
|
|
/* We can just emit function and compiler-generated variables statically;
|
|
having multiple copies is (for the most part) only a waste of space. */
|
|
TREE_PUBLIC (decl) = 0;
|
|
else if (DECL_INITIAL (decl) == NULL_TREE
|
|
|| DECL_INITIAL (decl) == error_mark_node)
|
|
/* Fallback, cannot have multiple copies. */
|
|
DECL_COMMON (decl) = 1;
|
|
|
|
if (TREE_PUBLIC (decl) && DECL_INSTANTIATED (decl))
|
|
DECL_COMDAT (decl) = 1;
|
|
}
|
|
|
|
/* Set DECL up to have the closest approximation of "weak" linkage. */
|
|
|
|
static void
|
|
d_weak_linkage (tree decl)
|
|
{
|
|
/* Weak definitions have to be public. */
|
|
gcc_assert (TREE_PUBLIC (decl));
|
|
|
|
/* Allow comdat linkage to be forced with the flag `-fno-weak-templates'. */
|
|
if (!flag_weak_templates || !TARGET_SUPPORTS_WEAK)
|
|
return d_comdat_linkage (decl);
|
|
|
|
declare_weak (decl);
|
|
}
|
|
|
|
/* DECL is a FUNCTION_DECL or a VAR_DECL with static storage. Set flags to
|
|
reflect the linkage that DECL will receive in the object file. */
|
|
|
|
void
|
|
set_linkage_for_decl (tree decl)
|
|
{
|
|
gcc_assert (VAR_OR_FUNCTION_DECL_P (decl) && TREE_STATIC (decl));
|
|
|
|
/* Non-public decls keep their internal linkage. */
|
|
if (!TREE_PUBLIC (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. */
|
|
if (targetdm.d_templates_always_comdat)
|
|
{
|
|
/* Make sure that instantiations are not removed. */
|
|
if (flag_weak_templates && DECL_INSTANTIATED (decl))
|
|
d_mark_needed (decl);
|
|
|
|
return d_comdat_linkage (decl);
|
|
}
|
|
|
|
/* Instantiated variables and functions need to be overridable by any other
|
|
symbol with the same name, so give them weak linkage. */
|
|
if (DECL_INSTANTIATED (decl))
|
|
return d_weak_linkage (decl);
|
|
|
|
/* Compiler generated public symbols can appear in multiple contexts. */
|
|
if (DECL_ARTIFICIAL (decl))
|
|
return d_weak_linkage (decl);
|
|
}
|
|
|
|
/* NODE is a FUNCTION_DECL, VAR_DECL or RECORD_TYPE for the declaration SYM.
|
|
Set flags to reflect visibility that NODE will get in the object file. */
|
|
|
|
void
|
|
set_visibility_for_decl (tree node, Dsymbol *sym)
|
|
{
|
|
Visibility visibility = sym->visible ();
|
|
if (visibility.kind == Visibility::private_)
|
|
TREE_PRIVATE (node) = 1;
|
|
else if (visibility.kind == Visibility::protected_)
|
|
TREE_PROTECTED (node) = 1;
|
|
|
|
/* If the declaration was declared `export', append either the dllimport
|
|
or dllexport attribute. */
|
|
if (TARGET_DLLIMPORT_DECL_ATTRIBUTES)
|
|
{
|
|
const char *attrname = NULL;
|
|
|
|
/* Have to test for import first. */
|
|
if (sym->isImportedSymbol ())
|
|
attrname = "dllimport";
|
|
else if (sym->isExport ())
|
|
attrname = "dllexport";
|
|
|
|
if (attrname != NULL)
|
|
{
|
|
if (DECL_P (node))
|
|
insert_decl_attribute (node, attrname);
|
|
else if (TYPE_P (node))
|
|
insert_type_attribute (node, attrname);
|
|
}
|
|
}
|
|
}
|