| /* d-frontend.cc -- D frontend interface to the gcc back-end. |
| Copyright (C) 2013-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/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<constructor_elt, va_gc> *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), |
| "%<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 (); |
| } |