Backports a change from upstream dmd that moves front-end NRVO checking from ReturnStatement semantic to the end of FuncDeclaration semantic. In the codegen, retStyle has been partially implemented so that only structs and static arrays return RETstack. This isn't accurate, but don't need to be for the purposes of semantic analysis. If a function either has TREE_ADDRESSABLE or must return in memory, then DECL_RESULT is set as the shidden field for the function. This is used in the codegen pass for ReturnStatement where it is now detected whether a function is returning a struct literal or a constructor function, then the DECL_RESULT is used to directly construct the return value, instead of doing so via temporaries. Reviewed-on: https://github.com/dlang/dmd/pull/11622 gcc/d/ChangeLog: PR d/96156 * d-frontend.cc (retStyle): Only return RETstack for struct and static array types. * decl.cc (DeclVisitor::visit (FuncDeclaration *)): Use NRVO return for all TREE_ADDRESSABLE types. Set shidden to the RESULT_DECL. * expr.cc (ExprVisitor::visit (CallExp *)): Force TARGET_EXPR if the 'this' pointer reference is a CONSTRUCTOR. (ExprVisitor::visit (StructLiteralExp *)): Generate assignment to the symbol to initialize with literal. * toir.cc (IRVisitor::visit (ReturnStatement *)): Detect returning struct literals and write directly into the RESULT_DECL. * dmd/MERGE: Merge upstream dmd fe5f388d8. gcc/testsuite/ChangeLog: PR d/96156 * gdc.dg/pr96156.d: New test.
252 lines
5.8 KiB
C++
252 lines
5.8 KiB
C++
/* d-frontend.cc -- D frontend interface to the gcc back-end.
|
|
Copyright (C) 2013-2020 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/declaration.h"
|
|
#include "dmd/expression.h"
|
|
#include "dmd/module.h"
|
|
#include "dmd/mtype.h"
|
|
#include "dmd/scope.h"
|
|
|
|
#include "tree.h"
|
|
#include "options.h"
|
|
#include "fold-const.h"
|
|
#include "diagnostic.h"
|
|
|
|
#include "d-tree.h"
|
|
|
|
|
|
/* Implements the Global interface defined by the frontend.
|
|
Used for managing the state of the current compilation. */
|
|
|
|
Global global;
|
|
|
|
void
|
|
Global::_init (void)
|
|
{
|
|
this->mars_ext = "d";
|
|
this->hdr_ext = "di";
|
|
this->doc_ext = "html";
|
|
this->ddoc_ext = "ddoc";
|
|
this->json_ext = "json";
|
|
this->obj_ext = "o";
|
|
|
|
this->run_noext = true;
|
|
this->version = "v"
|
|
#include "verstr.h"
|
|
;
|
|
|
|
this->stdmsg = stderr;
|
|
}
|
|
|
|
/* Start gagging. Return the current number of gagged errors. */
|
|
|
|
unsigned
|
|
Global::startGagging (void)
|
|
{
|
|
this->gag++;
|
|
return this->gaggedErrors;
|
|
}
|
|
|
|
/* End gagging, restoring the old gagged state. Return true if errors
|
|
occured while gagged. */
|
|
|
|
bool
|
|
Global::endGagging (unsigned oldGagged)
|
|
{
|
|
bool anyErrs = (this->gaggedErrors != oldGagged);
|
|
this->gag--;
|
|
|
|
/* Restore the original state of gagged errors; set total errors
|
|
to be original errors + new ungagged errors. */
|
|
this->errors -= (this->gaggedErrors - oldGagged);
|
|
this->gaggedErrors = oldGagged;
|
|
|
|
return anyErrs;
|
|
}
|
|
|
|
/* Increment the error count to record that an error has occured in the
|
|
current context. An error message may or may not have been printed. */
|
|
|
|
void
|
|
Global::increaseErrorCount (void)
|
|
{
|
|
if (gag)
|
|
this->gaggedErrors++;
|
|
|
|
this->errors++;
|
|
}
|
|
|
|
|
|
/* Implements the Loc interface defined by the frontend.
|
|
Used for keeping track of current file/line position in code. */
|
|
|
|
Loc::Loc (const char *filename, unsigned linnum, unsigned charnum)
|
|
{
|
|
this->linnum = linnum;
|
|
this->charnum = charnum;
|
|
this->filename = filename;
|
|
}
|
|
|
|
const char *
|
|
Loc::toChars (void) const
|
|
{
|
|
OutBuffer buf;
|
|
|
|
if (this->filename)
|
|
buf.printf ("%s", this->filename);
|
|
|
|
if (this->linnum)
|
|
{
|
|
buf.printf (":%u", this->linnum);
|
|
if (this->charnum)
|
|
buf.printf (":%u", this->charnum);
|
|
}
|
|
|
|
return buf.extractChars ();
|
|
}
|
|
|
|
bool
|
|
Loc::equals (const Loc &loc)
|
|
{
|
|
if (this->linnum != loc.linnum || this->charnum != loc.charnum)
|
|
return false;
|
|
|
|
if (!FileName::equals (this->filename, loc.filename))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Implements back-end specific interfaces used by the frontend. */
|
|
|
|
/* Determine return style of function - whether in registers or through a
|
|
hidden pointer to the caller's stack. */
|
|
|
|
RET
|
|
retStyle (TypeFunction *tf)
|
|
{
|
|
/* Need the backend type to determine this, but this is called from the
|
|
frontend before semantic processing is finished. An accurate value
|
|
is not currently needed anyway. */
|
|
if (tf->isref)
|
|
return RETregs;
|
|
|
|
Type *tn = tf->next->toBasetype ();
|
|
|
|
if (tn->ty == Tstruct || tn->ty == Tsarray)
|
|
return RETstack;
|
|
|
|
return RETregs;
|
|
}
|
|
|
|
/* Determine if function FD is a builtin one that we can evaluate in CTFE. */
|
|
|
|
BUILTIN
|
|
isBuiltin (FuncDeclaration *fd)
|
|
{
|
|
if (fd->builtin != BUILTINunknown)
|
|
return fd->builtin;
|
|
|
|
maybe_set_intrinsic (fd);
|
|
|
|
return fd->builtin;
|
|
}
|
|
|
|
/* Evaluate builtin D function FD whose argument list is ARGUMENTS.
|
|
Return result; NULL if cannot evaluate it. */
|
|
|
|
Expression *
|
|
eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
|
|
{
|
|
if (fd->builtin != BUILTINyes)
|
|
return NULL;
|
|
|
|
tree decl = get_symbol_decl (fd);
|
|
gcc_assert (fndecl_built_in_p (decl)
|
|
|| DECL_INTRINSIC_CODE (decl) != INTRINSIC_NONE);
|
|
|
|
TypeFunction *tf = fd->type->toTypeFunction ();
|
|
Expression *e = NULL;
|
|
input_location = make_location_t (loc);
|
|
|
|
tree result = d_build_call (tf, decl, NULL, arguments);
|
|
result = fold (result);
|
|
|
|
/* Builtin should be successfully evaluated.
|
|
Will only return NULL if we can't convert it. */
|
|
if (TREE_CONSTANT (result) && TREE_CODE (result) != CALL_EXPR)
|
|
e = d_eval_constant_expression (result);
|
|
|
|
return e;
|
|
}
|
|
|
|
/* Build and return typeinfo type for TYPE. */
|
|
|
|
Type *
|
|
getTypeInfoType (Loc loc, Type *type, Scope *sc)
|
|
{
|
|
if (!global.params.useTypeInfo)
|
|
{
|
|
/* Even when compiling without RTTI we should still be able to evaluate
|
|
TypeInfo at compile-time, just not at run-time. */
|
|
if (!sc || !(sc->flags & SCOPEctfe))
|
|
{
|
|
static int warned = 0;
|
|
|
|
if (!warned)
|
|
{
|
|
error_at (make_location_t (loc),
|
|
"%<object.TypeInfo%> cannot be used with %<-fno-rtti%>");
|
|
warned = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Type::dtypeinfo == NULL
|
|
|| (Type::dtypeinfo->storage_class & STCtemp))
|
|
{
|
|
/* If TypeInfo has not been declared, warn about each location once. */
|
|
static Loc warnloc;
|
|
|
|
if (!loc.equals (warnloc))
|
|
{
|
|
error_at (make_location_t (loc),
|
|
"%<object.TypeInfo%> could not be found, "
|
|
"but is implicitly used");
|
|
warnloc = loc;
|
|
}
|
|
}
|
|
|
|
gcc_assert (type->ty != Terror);
|
|
create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
|
|
return type->vtinfo->type;
|
|
}
|
|
|
|
/* Return an inlined copy of a default argument for a function parameter. */
|
|
|
|
Expression *
|
|
inlineCopy (Expression *e, Scope *)
|
|
{
|
|
return e->copy ();
|
|
}
|