/* 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/aggregate.h" #include "dmd/compiler.h" #include "dmd/declaration.h" #include "dmd/errors.h" #include "dmd/expression.h" #include "dmd/identifier.h" #include "dmd/module.h" #include "dmd/mtype.h" #include "dmd/scope.h" #include "dmd/statement.h" #include "dmd/target.h" #include "tree.h" #include "options.h" #include "fold-const.h" #include "diagnostic.h" #include "stor-layout.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; this->errorLimit = flag_max_errors; } /* 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.extractString (); } 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 the Port interface defined by the frontend. A mini library for doing compiler/system specific things. */ /* Compare the first N bytes of S1 and S2 without regard to the case. */ int Port::memicmp (const char *s1, const char *s2, size_t n) { int result = 0; for (size_t i = 0; i < n; i++) { char c1 = s1[i]; char c2 = s2[i]; result = c1 - c2; if (result) { result = TOUPPER (c1) - TOUPPER (c2); if (result) break; } } return result; } /* Convert all characters in S to uppercase. */ char * Port::strupr (char *s) { char *t = s; while (*s) { *s = TOUPPER (*s); s++; } return t; } /* Return true if the real_t value from string BUFFER overflows as a result of rounding down to float mode. */ bool Port::isFloat32LiteralOutOfRange (const char *buffer) { real_t r; real_from_string3 (&r.rv (), buffer, TYPE_MODE (float_type_node)); return r == Target::RealProperties::infinity; } /* Return true if the real_t value from string BUFFER overflows as a result of rounding down to double mode. */ bool Port::isFloat64LiteralOutOfRange (const char *buffer) { real_t r; real_from_string3 (&r.rv (), buffer, TYPE_MODE (double_type_node)); return r == Target::RealProperties::infinity; } /* Fetch a little-endian 16-bit value from BUFFER. */ unsigned Port::readwordLE (void *buffer) { unsigned char *p = (unsigned char*) buffer; return ((unsigned) p[1] << 8) | (unsigned) p[0]; } /* Fetch a big-endian 16-bit value from BUFFER. */ unsigned Port::readwordBE (void *buffer) { unsigned char *p = (unsigned char*) buffer; return ((unsigned) p[0] << 8) | (unsigned) p[1]; } /* Fetch a little-endian 32-bit value from BUFFER. */ unsigned Port::readlongLE (void *buffer) { unsigned char *p = (unsigned char*) buffer; return (((unsigned) p[3] << 24) | ((unsigned) p[2] << 16) | ((unsigned) p[1] << 8) | (unsigned) p[0]); } /* Fetch a big-endian 32-bit value from BUFFER. */ unsigned Port::readlongBE (void *buffer) { unsigned char *p = (unsigned char*) buffer; return (((unsigned) p[0] << 24) | ((unsigned) p[1] << 16) | ((unsigned) p[2] << 8) | (unsigned) p[3]); } /* Write an SZ-byte sized VALUE to BUFFER, ignoring endian-ness. */ void Port::valcpy (void *buffer, uint64_t value, size_t sz) { switch (sz) { case 1: *(uint8_t *) buffer = (uint8_t) value; break; case 2: *(uint16_t *) buffer = (uint16_t) value; break; case 4: *(uint32_t *) buffer = (uint32_t) value; break; case 8: *(uint64_t *) buffer = (uint64_t) value; break; default: gcc_unreachable (); } } /* Implements the CTFloat interface defined by the frontend. Compile-time floating-pointer helper functions. */ /* Return the absolute value of R. */ real_t CTFloat::fabs (real_t r) { real_t x; real_arithmetic (&x.rv (), ABS_EXPR, &r.rv (), NULL); return x.normalize (); } /* Return the value of R * 2 ^^ EXP. */ real_t CTFloat::ldexp (real_t r, int exp) { real_t x; real_ldexp (&x.rv (), &r.rv (), exp); return x.normalize (); } /* Return true if longdouble value X is identical to Y. */ bool CTFloat::isIdentical (real_t x, real_t y) { real_value rx = x.rv (); real_value ry = y.rv (); return (REAL_VALUE_ISNAN (rx) && REAL_VALUE_ISNAN (ry)) || real_identical (&rx, &ry); } /* Return true if real_t value R is NaN. */ bool CTFloat::isNaN (real_t r) { return REAL_VALUE_ISNAN (r.rv ()); } /* Same as isNaN, but also check if is signalling. */ bool CTFloat::isSNaN (real_t r) { return REAL_VALUE_ISSIGNALING_NAN (r.rv ()); } /* Return true if real_t value is +Inf. */ bool CTFloat::isInfinity (real_t r) { return REAL_VALUE_ISINF (r.rv ()); } /* Return a real_t value from string BUFFER rounded to long double mode. */ real_t CTFloat::parse (const char *buffer, bool *overflow) { real_t r; real_from_string3 (&r.rv (), buffer, TYPE_MODE (long_double_type_node)); /* Front-end checks overflow to see if the value is representable. */ if (overflow && r == Target::RealProperties::infinity) *overflow = true; return r; } /* Format the real_t value R to string BUFFER as a decimal or hexadecimal, converting the result to uppercase if FMT requests it. */ int CTFloat::sprint (char *buffer, char fmt, real_t r) { if (fmt == 'a' || fmt == 'A') { /* Converting to a hexadecimal string. */ real_to_hexadecimal (buffer, &r.rv (), 32, 0, 1); int buflen; switch (fmt) { case 'A': buflen = strlen (buffer); for (int i = 0; i < buflen; i++) buffer[i] = TOUPPER (buffer[i]); return buflen; case 'a': return strlen (buffer); default: gcc_unreachable (); } } else { /* Note: restricting the precision of significant digits to 18. */ real_to_decimal (buffer, &r.rv (), 32, 18, 1); return strlen (buffer); } } /* Return a hash value for real_t value R. */ size_t CTFloat::hash (real_t r) { return real_hash (&r.rv ()); } /* Implements the Compiler interface used by the frontend. */ /* Generate C main() in response to seeing D main(). This used to be in libdruntime, but contained a reference to _Dmain which didn't work when druntime was made into a shared library and was linked to a program, such as a C++ program, that didn't have a _Dmain. */ void Compiler::genCmain (Scope *sc) { static bool initialized = false; if (initialized) return; /* The D code to be generated is provided by __entrypoint.di, try to load it, but don't fail if unfound. */ unsigned errors = global.startGagging (); Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__entrypoint")); if (global.endGagging (errors)) m = NULL; if (m != NULL) { m->importedFrom = m; m->importAll (NULL); m->semantic (NULL); m->semantic2 (NULL); m->semantic3 (NULL); d_add_entrypoint_module (m, sc->_module); } initialized = true; } /* Perform a reinterpret cast of EXPR to type TYPE for use in CTFE. The front end should have already ensured that EXPR is a constant, so we just lower the value to GCC and return the converted CST. */ Expression * Compiler::paintAsType (UnionExp *, Expression *expr, Type *type) { /* We support up to 512-bit values. */ unsigned char buffer[64]; tree cst; Type *tb = type->toBasetype (); if (expr->type->isintegral ()) cst = build_integer_cst (expr->toInteger (), build_ctype (expr->type)); else if (expr->type->isfloating ()) cst = build_float_cst (expr->toReal (), expr->type); else if (expr->op == TOKarrayliteral) { /* Build array as VECTOR_CST, assumes EXPR is constant. */ Expressions *elements = ((ArrayLiteralExp *) expr)->elements; vec *elms = NULL; vec_safe_reserve (elms, elements->dim); for (size_t i = 0; i < elements->dim; i++) { Expression *e = (*elements)[i]; if (e->type->isintegral ()) { tree value = build_integer_cst (e->toInteger (), build_ctype (e->type)); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } else if (e->type->isfloating ()) { tree value = build_float_cst (e->toReal (), e->type); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } else gcc_unreachable (); } /* Build vector type. */ int nunits = ((TypeSArray *) expr->type)->dim->toUInteger (); Type *telem = expr->type->nextOf (); tree vectype = build_vector_type (build_ctype (telem), nunits); cst = build_vector_from_ctor (vectype, elms); } else gcc_unreachable (); /* Encode CST to buffer. */ int len = native_encode_expr (cst, buffer, sizeof (buffer)); if (tb->ty == Tsarray) { /* Interpret value as a vector of the same size, then return the array literal. */ int nunits = ((TypeSArray *) type)->dim->toUInteger (); Type *elem = type->nextOf (); tree vectype = build_vector_type (build_ctype (elem), nunits); cst = native_interpret_expr (vectype, buffer, len); Expression *e = d_eval_constant_expression (cst); gcc_assert (e != NULL && e->op == TOKvector); return ((VectorExp *) e)->e1; } else { /* Normal interpret cast. */ cst = native_interpret_expr (build_ctype (type), buffer, len); Expression *e = d_eval_constant_expression (cst); gcc_assert (e != NULL); return e; } } /* Check imported module M for any special processing. Modules we look out for are: - object: For D runtime type information. - gcc.builtins: For all gcc builtins. - core.stdc.*: For all gcc library builtins. */ void Compiler::loadModule (Module *m) { ModuleDeclaration *md = m->md; if (!md || !md->id || !md->packages) { Identifier *id = (md && md->id) ? md->id : m->ident; if (!strcmp (id->toChars (), "object")) create_tinfo_types (m); } else if (md->packages->dim == 1) { if (!strcmp ((*md->packages)[0]->toChars (), "gcc") && !strcmp (md->id->toChars (), "builtins")) d_build_builtins_module (m); } else if (md->packages->dim == 2) { if (!strcmp ((*md->packages)[0]->toChars (), "core") && !strcmp ((*md->packages)[1]->toChars (), "stdc")) d_add_builtin_module (m); } } /* 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 *) { /* 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. */ return RETstack; } /* 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 = (TypeFunction *) fd->type; 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), "% 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), "% 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 (); }