| /* decl.cc -- Lower D frontend declarations to GCC trees. |
| Copyright (C) 2006-2019 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 "d-tree.h" |
| |
| |
| /* Return identifier for the external mangled name of DECL. */ |
| |
| static const char * |
| mangle_decl (Dsymbol *decl) |
| { |
| if (decl->isFuncDeclaration ()) |
| return mangleExact ((FuncDeclaration *)decl); |
| else |
| { |
| OutBuffer buf; |
| mangleToBuffer (decl, &buf); |
| return buf.extractString (); |
| } |
| } |
| |
| /* 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 = 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.attribute module. */ |
| |
| static bool |
| gcc_attribute_p (Dsymbol *decl) |
| { |
| ModuleDeclaration *md = decl->getModule ()->md; |
| |
| if (md && md->packages && md->packages->dim == 1) |
| { |
| if (!strcmp ((*md->packages)[0]->toChars (), "gcc") |
| && !strcmp (md->id->toChars (), "attribute")) |
| 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; |
| } |
| |
| /* This should be overridden by each declaration class. */ |
| |
| void visit (Dsymbol *) |
| { |
| } |
| |
| /* Compile a D module, and all members of it. */ |
| |
| void visit (Module *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| build_module_tree (d); |
| d->semanticRun = PASSobj; |
| } |
| |
| /* Write the imported symbol to debug. */ |
| |
| void visit (Import *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| 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.dim; 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 = PASSobj; |
| } |
| |
| /* Expand any local variables found in tuples. */ |
| |
| void visit (TupleDeclaration *d) |
| { |
| for (size_t i = 0; i < d->objects->dim; i++) |
| { |
| RootObject *o = (*d->objects)[i]; |
| if ((o->dyncast () == DYNCAST_EXPRESSION) |
| && ((Expression *) o)->op == TOKdsymbol) |
| { |
| Declaration *d = ((DsymbolExp *) o)->s->isDeclaration (); |
| if (d) |
| d->accept (this); |
| } |
| } |
| } |
| |
| /* Walk over all declarations in the attribute scope. */ |
| |
| void visit (AttribDeclaration *d) |
| { |
| Dsymbols *ds = d->include (NULL, NULL); |
| |
| if (!ds) |
| return; |
| |
| for (size_t i = 0; i < ds->dim; i++) |
| { |
| Dsymbol *s = (*ds)[i]; |
| s->accept (this); |
| } |
| } |
| |
| /* Pragmas are a way to pass special information to the compiler and to add |
| vendor specific extensions to D. We don't do anything here, yet. */ |
| |
| void visit (PragmaDeclaration *d) |
| { |
| if (!global.params.ignoreUnsupportedPragmas) |
| { |
| if (d->ident == Identifier::idPool ("lib") |
| || d->ident == Identifier::idPool ("startaddress")) |
| { |
| 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) |
| { |
| 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) |
| { |
| if (isError (d) || !d->members) |
| return; |
| |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *s = (*d->members)[i]; |
| s->accept (this); |
| } |
| } |
| |
| /* 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) |
| { |
| /* 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 == Tarray || tb->ty == Tpointer) |
| tb = tb->nextOf ()->baseElemOf (); |
| |
| TemplateInstance *ti = NULL; |
| |
| if (tb->ty == Tstruct) |
| ti = ((TypeStruct *) tb)->sym->isInstantiated (); |
| else if (tb->ty == Tclass) |
| ti = ((TypeClass *) tb)->sym->isInstantiated (); |
| |
| /* Return type is instantiated from this template declaration, walk over |
| all members of the instance. */ |
| if (ti && ti->tempdecl == d) |
| ti->accept (this); |
| } |
| |
| /* Walk over all members in the instantiated template. */ |
| |
| void visit (TemplateInstance *d) |
| { |
| if (isError (d)|| !d->members) |
| return; |
| |
| if (!d->needsCodegen ()) |
| return; |
| |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *s = (*d->members)[i]; |
| s->accept (this); |
| } |
| } |
| |
| /* Walk over all members in the mixin template scope. */ |
| |
| void visit (TemplateMixin *d) |
| { |
| if (isError (d)|| !d->members) |
| return; |
| |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *s = (*d->members)[i]; |
| s->accept (this); |
| } |
| } |
| |
| /* Write out compiler generated TypeInfo, initializer and functions for the |
| given struct declaration, walking over all static members. */ |
| |
| void visit (StructDeclaration *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| if (d->type->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 (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; |
| |
| /* Don't emit any symbols from gcc.attribute module. */ |
| if (gcc_attribute_p (d)) |
| return; |
| |
| /* Generate TypeInfo. */ |
| if (have_typeinfo_p (Type::dtypeinfo)) |
| create_typeinfo (d->type, NULL); |
| |
| /* Generate static initializer. */ |
| d->sinit = aggregate_initializer_decl (d); |
| DECL_INITIAL (d->sinit) = layout_struct_initializer (d); |
| |
| if (d->isInstantiated ()) |
| d_linkonce_linkage (d->sinit); |
| |
| d_finish_decl (d->sinit); |
| |
| /* Put out the members. */ |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *member = (*d->members)[i]; |
| /* There might be static ctors in the members, and they cannot |
| be put in separate object files. */ |
| member->accept (this); |
| } |
| |
| /* Put out xopEquals, xopCmp and xopHash. */ |
| if (d->xeq && d->xeq != d->xerreq) |
| d->xeq->accept (this); |
| |
| if (d->xcmp && d->xcmp != d->xerrcmp) |
| d->xcmp->accept (this); |
| |
| if (d->xhash) |
| d->xhash->accept (this); |
| |
| d->semanticRun = PASSobj; |
| } |
| |
| /* 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.dim; 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.dim; 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) || fd2->leastAsSpecialized (fd)) |
| { |
| TypeFunction *tf = (TypeFunction *) fd->type; |
| if (tf->ty == Tfunction) |
| { |
| 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 ()); |
| } |
| else |
| { |
| error_at (make_location_t (fd->loc), "use of %qs", |
| fd->toPrettyChars ()); |
| inform (make_location_t (fd2->loc), "is hidden by %qs", |
| fd2->toPrettyChars ()); |
| } |
| |
| 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) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| if (d->type->ty == Terror) |
| { |
| error_at (make_location_t (d->loc), |
| "had semantic errors when compiling"); |
| return; |
| } |
| |
| if (!d->members) |
| return; |
| |
| /* Put out the members. */ |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *member = (*d->members)[i]; |
| member->accept (this); |
| } |
| |
| /* 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); |
| d->vtblsym = get_vtable_decl (d); |
| d->sinit = aggregate_initializer_decl (d); |
| |
| /* Generate static initializer. */ |
| DECL_INITIAL (d->sinit) = layout_class_initializer (d); |
| d_linkonce_linkage (d->sinit); |
| d_finish_decl (d->sinit); |
| |
| /* Put out the TypeInfo. */ |
| if (have_typeinfo_p (Type::dtypeinfo)) |
| create_typeinfo (d->type, NULL); |
| |
| DECL_INITIAL (d->csym) = layout_classinfo (d); |
| d_linkonce_linkage (d->csym); |
| 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.dim; 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 (d->vtblsym) |
| = build_constructor (TREE_TYPE (d->vtblsym), elms); |
| d_comdat_linkage (d->vtblsym); |
| d_finish_decl (d->vtblsym); |
| |
| /* 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)); |
| |
| d->semanticRun = PASSobj; |
| } |
| |
| /* Write out compiler generated TypeInfo and vtables for the given interface |
| declaration, walking over all static members. */ |
| |
| void visit (InterfaceDeclaration *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| if (d->type->ty == Terror) |
| { |
| error_at (make_location_t (d->loc), |
| "had semantic errors when compiling"); |
| return; |
| } |
| |
| if (!d->members) |
| return; |
| |
| /* Put out the members. */ |
| for (size_t i = 0; i < d->members->dim; i++) |
| { |
| Dsymbol *member = (*d->members)[i]; |
| member->accept (this); |
| } |
| |
| /* Generate C symbols. */ |
| d->csym = get_classinfo_decl (d); |
| |
| /* Put out the TypeInfo. */ |
| if (have_typeinfo_p (Type::dtypeinfo)) |
| { |
| create_typeinfo (d->type, NULL); |
| d->type->vtinfo->accept (this); |
| } |
| |
| DECL_INITIAL (d->csym) = layout_classinfo (d); |
| d_linkonce_linkage (d->csym); |
| d_finish_decl (d->csym); |
| |
| /* 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)); |
| |
| d->semanticRun = PASSobj; |
| } |
| |
| /* Write out compiler generated TypeInfo and initializer for the given |
| enum declaration. */ |
| |
| void visit (EnumDeclaration *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| if (d->errors || d->type->ty == Terror) |
| { |
| error_at (make_location_t (d->loc), |
| "had semantic errors when compiling"); |
| return; |
| } |
| |
| if (d->isAnonymous ()) |
| return; |
| |
| /* Generate TypeInfo. */ |
| if (have_typeinfo_p (Type::dtypeinfo)) |
| create_typeinfo (d->type, NULL); |
| |
| TypeEnum *tc = (TypeEnum *) d->type; |
| 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); |
| |
| if (d->isInstantiated ()) |
| d_linkonce_linkage (d->sinit); |
| |
| d_finish_decl (d->sinit); |
| |
| /* 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)); |
| } |
| |
| d->semanticRun = PASSobj; |
| } |
| |
| /* 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) |
| { |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| if (d->type->ty == Terror) |
| { |
| error_at (make_location_t (d->loc), |
| "had semantic errors when compiling"); |
| return; |
| } |
| |
| if (d->aliassym) |
| { |
| d->toAlias ()->accept (this); |
| 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 () && !(d->storage_class & STCextern)) |
| { |
| tree decl = get_symbol_decl (d); |
| |
| /* 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 && !d->_init->isVoidInitializer ()) |
| { |
| Expression *e = initializerToExpression (d->_init, d->type); |
| DECL_INITIAL (decl) = build_expr (e, true); |
| } |
| else |
| { |
| if (d->type->ty == Tstruct) |
| { |
| StructDeclaration *sd = ((TypeStruct *) d->type)->sym; |
| DECL_INITIAL (decl) = layout_struct_initializer (sd); |
| } |
| 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 ()->ty == Tsarray); |
| |
| 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) |
| { |
| tree decl = get_symbol_decl (d); |
| |
| if (!d->_init->isVoidInitializer ()) |
| { |
| 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); |
| } |
| else if (d->size (d->loc) != 0) |
| { |
| /* Zero-length arrays do not have an initializer. */ |
| warning (OPT_Wuninitialized, "uninitialized variable '%s'", |
| d->ident ? d->ident->toChars () : "(no name)"); |
| } |
| } |
| } |
| |
| d->semanticRun = PASSobj; |
| } |
| |
| /* Generate and compile a static TypeInfo declaration, but only if it is |
| needed in the current compilation. */ |
| |
| void visit (TypeInfoDeclaration *d) |
| { |
| if (d->semanticRun >= PASSobj) |
| 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 = PASSobj; |
| } |
| |
| /* Finish up a function declaration and compile it all the way |
| down to assembler language output. */ |
| |
| void visit (FuncDeclaration *d) |
| { |
| /* Already generated the function. */ |
| if (d->semanticRun >= PASSobj) |
| return; |
| |
| /* Don't emit any symbols from gcc.attribute 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 (d->type->ty == Tfunction) |
| { |
| TypeFunction *tf = (TypeFunction *) d->type; |
| if (tf->next == NULL || tf->next->ty == Terror) |
| return; |
| } |
| |
| if (d->semantic3Errors) |
| 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->semantic3Errors) |
| return; |
| } |
| } |
| |
| /* Ensure all semantic passes have run. */ |
| if (d->semanticRun < PASSsemantic3) |
| { |
| d->functionSemantic3 (); |
| Module::runDeferredSemantic3 (); |
| } |
| |
| if (global.errors) |
| return; |
| |
| /* 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 ()); |
| |
| /* Start generating code for this function. */ |
| gcc_assert (d->semanticRun == PASSsemantic3done); |
| d->semanticRun = PASSobj; |
| |
| 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. */ |
| size_t n_parameters = d->parameters ? d->parameters->dim : 0; |
| |
| for (size_t i = 0; i < n_parameters; i++) |
| { |
| VarDeclaration *param = (*d->parameters)[i]; |
| parm_decl = get_symbol_decl (param); |
| /* 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; |
| } |
| } |
| } |
| |
| /* May change cfun->static_chain. */ |
| build_closure (d); |
| |
| if (d->vresult) |
| declare_local_var (d->vresult); |
| |
| if (d->v_argptr) |
| push_stmt_list (); |
| |
| /* 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. */ |
| if (d->nrvo_can) |
| { |
| tree restype = TREE_TYPE (DECL_RESULT (fndecl)); |
| |
| if (!AGGREGATE_TYPE_P (restype)) |
| d->nrvo_can = 0; |
| else |
| d->nrvo_can = aggregate_value_p (restype, fndecl); |
| } |
| |
| if (d->nrvo_can) |
| { |
| tree resdecl = DECL_RESULT (fndecl); |
| |
| TREE_TYPE (resdecl) |
| = build_reference_type (TREE_TYPE (resdecl)); |
| DECL_BY_REFERENCE (resdecl) = 1; |
| TREE_ADDRESSABLE (resdecl) = 0; |
| relayout_decl (resdecl); |
| |
| if (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; |
| resdecl = build_deref (resdecl); |
| |
| SET_DECL_VALUE_EXPR (var, resdecl); |
| DECL_HAS_VALUE_EXPR_P (var) = 1; |
| SET_DECL_LANG_NRVO (var, resdecl); |
| } |
| } |
| |
| 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 (); |
| d->accept (&v); |
| |
| input_location = saved_location; |
| } |
| |
| /* Return the decl for the symbol, create it if it doesn't already exist. */ |
| |
| tree |
| get_symbol_decl (Declaration *decl) |
| { |
| if (decl->csym) |
| return decl->csym; |
| |
| /* 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); |
| |
| if (!fd->fbody) |
| 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 != STRUCTALIGN_DEFAULT) |
| { |
| SET_DECL_ALIGN (decl->csym, vd->alignment * 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) |
| mangled_name = get_identifier (decl->mangleOverride); |
| else |
| mangled_name = get_identifier (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 (!DECL_EXTERNAL (decl->csym)) |
| { |
| if (IDENTIFIER_DSYMBOL (mangled_name)) |
| { |
| Declaration *other = IDENTIFIER_DSYMBOL (mangled_name); |
| |
| /* Non-templated variables shouldn't be defined twice. */ |
| if (!decl->isInstantiated ()) |
| ScopeDsymbol::multiplyDefined (decl->loc, decl, other); |
| |
| decl->csym = get_symbol_decl (other); |
| return decl->csym; |
| } |
| |
| 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) |
| { |
| /* 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. */ |
| if (fd->isMember2 () || fd->isFuncLiteralDeclaration ()) |
| { |
| /* See grokmethod in cp/decl.c. Maybe we shouldn't be setting inline |
| flags without reason or proper handling. */ |
| DECL_DECLARED_INLINE_P (decl->csym) = 1; |
| DECL_NO_INLINE_WARNING_P (decl->csym) = 1; |
| } |
| |
| /* Function was declared 'naked'. */ |
| if (fd->naked) |
| { |
| insert_decl_attribute (decl->csym, "naked"); |
| DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl->csym) = 1; |
| } |
| |
| /* Vector array operations are always compiler generated. */ |
| if (fd->isArrayOp) |
| { |
| TREE_PUBLIC (decl->csym) = 1; |
| DECL_ARTIFICIAL (decl->csym) = 1; |
| DECL_DECLARED_INLINE_P (decl->csym) = 1; |
| d_comdat_linkage (decl->csym); |
| } |
| |
| /* And so are ensure and require contracts. */ |
| if (fd->ident == Identifier::idPool ("ensure") |
| || fd->ident == Identifier::idPool ("require")) |
| { |
| DECL_ARTIFICIAL (decl->csym) = 1; |
| TREE_PUBLIC (decl->csym) = 1; |
| } |
| |
| if (decl->storage_class & STCfinal) |
| DECL_FINAL_P (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 (node->origin) |
| node->unnest (); |
| } |
| |
| /* 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; |
| |
| /* Protection attributes are used by the debugger. */ |
| if (decl->protection.kind == PROTprivate) |
| TREE_PRIVATE (decl->csym) = 1; |
| else if (decl->protection.kind == PROTprotected) |
| TREE_PROTECTED (decl->csym) = 1; |
| |
| /* Likewise, so could the deprecated attribute. */ |
| if (decl->storage_class & STCdeprecated) |
| TREE_DEPRECATED (decl->csym) = 1; |
| |
| #if TARGET_DLLIMPORT_DECL_ATTRIBUTES |
| /* Have to test for import first. */ |
| if (decl->isImportedSymbol ()) |
| { |
| insert_decl_attribute (decl->csym, "dllimport"); |
| DECL_DLLIMPORT_P (decl->csym) = 1; |
| } |
| else if (decl->isExport ()) |
| insert_decl_attribute (decl->csym, "dllexport"); |
| #endif |
| |
| 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; |
| |
| if (decl->isInstantiated ()) |
| d_linkonce_linkage (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. */ |
| if (decl->userAttribDecl) |
| { |
| Expressions *attrs = decl->userAttribDecl->getAttributes (); |
| decl_attributes (&decl->csym, build_attributes (attrs), 0); |
| } |
| else if (DECL_ATTRIBUTES (decl->csym) != NULL) |
| decl_attributes (&decl->csym, DECL_ATTRIBUTES (decl->csym), 0); |
| |
| /* %% Probably should be a little more intelligent about setting this. */ |
| TREE_USED (decl->csym) = 1; |
| d_keep (decl->csym); |
| |
| return decl->csym; |
| } |
| |
| /* 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; |
| |
| 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 = build_decl (input_location, VAR_DECL, NULL_TREE, type); |
| |
| DECL_CONTEXT (decl) = current_function_decl; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_IGNORED_P (decl) = 1; |
| 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 named return value. */ |
| if (DECL_LANG_NRVO (t)) |
| return DECL_LANG_NRVO (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); |
| |
| return component_ref (build_deref (frame_ref), |
| DECL_LANG_FRAME_FIELD (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); |
| |
| 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, DECL_SECTION_NAME (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); |
| |
| /* Target assemble_mi_thunk doesn't work across section boundaries |
| on many targets, instead force thunk to be expanded in gimple. */ |
| if (DECL_EXTERNAL (function)) |
| { |
| /* cgraph::expand_thunk writes over current_function_decl, so if this |
| could ever be in use by the codegen pass, we want to know about it. */ |
| gcc_assert (current_function_decl == NULL_TREE); |
| |
| if (!stdarg_p (TREE_TYPE (thunk))) |
| { |
| thunk_node->create_edge (funcn, NULL, thunk_node->count); |
| thunk_node->expand_thunk (false, true); |
| } |
| |
| /* Tell the back-end to not bother inlining the function, this is |
| assumed not to work as it could be referencing symbols outside |
| of the current compilation unit. */ |
| DECL_UNINLINABLE (function) = 1; |
| } |
| } |
| |
| /* 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 targetting. |
| When the function is outside the current compilation unit however, then the |
| thunk must be kept private to not conflict. */ |
| TREE_PUBLIC (thunk) = TREE_PUBLIC (function) && !DECL_EXTERNAL (function); |
| |
| DECL_EXTERNAL (thunk) = 0; |
| |
| /* Thunks are always addressable. */ |
| TREE_ADDRESSABLE (thunk) = 1; |
| TREE_USED (thunk) = 1; |
| DECL_ARTIFICIAL (thunk) = 1; |
| DECL_DECLARED_INLINE_P (thunk) = 0; |
| |
| DECL_VISIBILITY (thunk) = DECL_VISIBILITY (function); |
| DECL_COMDAT (thunk) = DECL_COMDAT (function); |
| DECL_WEAK (thunk) = DECL_WEAK (function); |
| |
| tree target_name = DECL_ASSEMBLER_NAME (function); |
| unsigned identlen = IDENTIFIER_LENGTH (target_name) + 14; |
| const char *ident = XNEWVEC (const char, identlen); |
| snprintf (CONST_CAST (char *, ident), identlen, |
| "_DT%u%s", offset, IDENTIFIER_POINTER (target_name)); |
| |
| DECL_NAME (thunk) = get_identifier (ident); |
| SET_DECL_ASSEMBLER_NAME (thunk, DECL_NAME (thunk)); |
| |
| d_keep (thunk); |
| |
| 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. */ |
| TemplateInstance *ti = fd->isInstantiated (); |
| if (ti && ti->needsCodegen ()) |
| { |
| /* Warn about templates instantiated in this compilation. */ |
| if (ti == fd->parent) |
| { |
| warning (OPT_Wtemplates, "%s %qs instantiated", |
| ti->kind (), ti->toPrettyChars (false)); |
| } |
| |
| DECL_EXTERNAL (fndecl) = 0; |
| } |
| else |
| { |
| Module *md = fd->getModule (); |
| if (md && md->isRoot ()) |
| 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. */ |
| |
| void |
| 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 offset to the BC's vtbl[] initializer from the start of CD. |
| Returns "~0u" if the base class is not found in any vtable interfaces. */ |
| |
| unsigned |
| base_vtable_offset (ClassDeclaration *cd, BaseClass *bc) |
| { |
| unsigned csymoffset = Target::classinfosize; |
| unsigned interfacesize = int_size_in_bytes (vtbl_interface_type_node); |
| csymoffset += cd->vtblInterfaces->dim * interfacesize; |
| |
| for (size_t i = 0; i < cd->vtblInterfaces->dim; i++) |
| { |
| BaseClass *b = (*cd->vtblInterfaces)[i]; |
| if (b == bc) |
| return csymoffset; |
| csymoffset += b->sym->vtbl.dim * Target::ptrsize; |
| } |
| |
| /* Check all overriding interface vtbl[]s. */ |
| for (ClassDeclaration *cd2 = cd->baseClass; cd2; cd2 = cd2->baseClass) |
| { |
| for (size_t k = 0; k < cd2->vtblInterfaces->dim; k++) |
| { |
| BaseClass *bs = (*cd2->vtblInterfaces)[k]; |
| if (bs->fillVtbl (cd, NULL, 0)) |
| { |
| if (bc == bs) |
| return csymoffset; |
| csymoffset += bs->sym->vtbl.dim * Target::ptrsize; |
| } |
| } |
| } |
| |
| return ~0u; |
| } |
| |
| /* 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) |
| return decl->vtblsym; |
| |
| 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.dim)); |
| |
| decl->vtblsym = declare_extern_var (ident, type); |
| DECL_LANG_SPECIFIC (decl->vtblsym) = build_lang_decl (NULL); |
| |
| /* Class is a reference, want the record type. */ |
| DECL_CONTEXT (decl->vtblsym) = TREE_TYPE (build_ctype (decl->type)); |
| TREE_READONLY (decl->vtblsym) = 1; |
| DECL_VIRTUAL_P (decl->vtblsym) = 1; |
| |
| SET_DECL_ALIGN (decl->vtblsym, TARGET_VTABLE_ENTRY_ALIGN); |
| DECL_USER_ALIGN (decl->vtblsym) = true; |
| |
| return decl->vtblsym; |
| } |
| |
| /* 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->dim; 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.dim; 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 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"); |
| |
| decl->sinit = declare_extern_var (ident, type); |
| DECL_LANG_SPECIFIC (decl->sinit) = build_lang_decl (NULL); |
| |
| DECL_CONTEXT (decl->sinit) = type; |
| TREE_READONLY (decl->sinit) = 1; |
| |
| /* Honor struct alignment set by user. */ |
| if (sd && sd->alignment != STRUCTALIGN_DEFAULT) |
| { |
| SET_DECL_ALIGN (decl->sinit, sd->alignment * BITS_PER_UNIT); |
| DECL_USER_ALIGN (decl->sinit) = true; |
| } |
| |
| return decl->sinit; |
| } |
| |
| /* Generate the data for the static initializer. */ |
| |
| tree |
| layout_class_initializer (ClassDeclaration *cd) |
| { |
| NewExp *ne = NewExp::create (cd->loc, NULL, NULL, cd->type, NULL); |
| ne->type = cd->type; |
| |
| Expression *e = ne->ctfeInterpret (); |
| gcc_assert (e->op == TOKclassreference); |
| |
| return build_class_instance ((ClassReferenceExp *) e); |
| } |
| |
| 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; |
| |
| tree type = build_ctype (decl->type); |
| |
| Identifier *ident_save = decl->ident; |
| if (!decl->ident) |
| decl->ident = Identifier::generateId ("__enum"); |
| tree ident = mangle_internal_decl (decl, "__init", "Z"); |
| decl->ident = ident_save; |
| |
| 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; |
| |
| gcc_assert (!POINTER_TYPE_P (type)); |
| |
| /* 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 (mangle_decl (dsym))); |
| TREE_PUBLIC (decl) = 1; |
| DECL_ARTIFICIAL (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)) |
| TYPE_STUB_DECL (type) = decl; |
| |
| rest_of_decl_compilation (decl, SCOPE_FILE_SCOPE_P (decl), 0); |
| } |
| |
| /* 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. */ |
| |
| void |
| d_comdat_linkage (tree decl) |
| { |
| if (flag_weak) |
| make_decl_one_only (decl, d_comdat_group (decl)); |
| else if (TREE_CODE (decl) == FUNCTION_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_COMDAT (decl) = 1; |
| } |
| |
| /* Set DECL up to have the closest approximation of "linkonce" linkage. */ |
| |
| void |
| d_linkonce_linkage (tree decl) |
| { |
| /* Weak definitions have to be public. */ |
| if (!TREE_PUBLIC (decl)) |
| return; |
| |
| /* Necessary to allow DECL_ONE_ONLY or DECL_WEAK functions to be inlined. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| DECL_DECLARED_INLINE_P (decl) = 1; |
| |
| /* No weak support, fallback to COMDAT linkage. */ |
| if (!flag_weak) |
| return d_comdat_linkage (decl); |
| |
| make_decl_one_only (decl, d_comdat_group (decl)); |
| } |